aboutsummaryrefslogtreecommitdiff
path: root/src/transport/plugin_transport_udp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/transport/plugin_transport_udp.c')
-rw-r--r--src/transport/plugin_transport_udp.c592
1 files changed, 592 insertions, 0 deletions
diff --git a/src/transport/plugin_transport_udp.c b/src/transport/plugin_transport_udp.c
new file mode 100644
index 000000000..ccaf9fbd1
--- /dev/null
+++ b/src/transport/plugin_transport_udp.c
@@ -0,0 +1,592 @@
1/*
2 This file is part of GNUnet
3 (C) 2001, 2002, 2003, 2004, 2005, 2008 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19*/
20
21/**
22 * @file transports/udp.c
23 * @brief Implementation of the UDP transport service
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
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"
33#include "ip.h"
34
35#define DEBUG_UDP GNUNET_YES
36
37/**
38 * The default maximum size of each outbound UDP message,
39 * optimal value for Ethernet (10 or 100 MBit).
40 */
41#define MESSAGE_SIZE 1472
42
43/**
44 * Message-Packet header.
45 */
46typedef struct
47{
48 /**
49 * size of the message, in bytes, including this header.
50 */
51 GNUNET_MessageHeader header;
52
53 /**
54 * What is the identity of the sender (GNUNET_hash of public key)
55 */
56 GNUNET_PeerIdentity sender;
57
58} UDPMessage;
59
60#define MY_TRANSPORT_NAME "UDP"
61#include "common.c"
62
63/* *********** globals ************* */
64
65static int stat_bytesReceived;
66
67static int stat_bytesSent;
68
69static int stat_bytesDropped;
70
71static int stat_udpConnected;
72
73/**
74 * thread that listens for inbound messages
75 */
76static struct GNUNET_SelectHandle *selector;
77
78/**
79 * the socket that we transmit all data with
80 */
81static struct GNUNET_SocketHandle *udp_sock;
82
83static struct GNUNET_LoadMonitor *load_monitor;
84
85
86/**
87 * The socket of session has data waiting, process!
88 *
89 * This function may only be called if the tcplock is
90 * already held by the caller.
91 */
92static int
93select_message_handler (void *mh_cls,
94 struct GNUNET_SelectHandle *sh,
95 struct GNUNET_SocketHandle *sock,
96 void *sock_ctx, const GNUNET_MessageHeader * msg)
97{
98 unsigned int len;
99 GNUNET_TransportPacket *mp;
100 const UDPMessage *um;
101
102 len = ntohs (msg->size);
103 if (len <= sizeof (UDPMessage))
104 {
105 GNUNET_GE_LOG (coreAPI->ectx,
106 GNUNET_GE_WARNING | GNUNET_GE_USER | GNUNET_GE_BULK,
107 _("Received malformed message via %s. Ignored.\n"),
108 "UDP");
109 return GNUNET_SYSERR;
110 }
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);
117 mp->tsession = NULL;
118 coreAPI->receive (mp);
119 if (stats != NULL)
120 stats->change (stat_bytesReceived, len);
121 return GNUNET_OK;
122}
123
124static void *
125select_accept_handler (void *ah_cls,
126 struct GNUNET_SelectHandle *sh,
127 struct GNUNET_SocketHandle *sock,
128 const void *addr, unsigned int addr_len)
129{
130 static int nonnullpointer;
131
132 if (GNUNET_NO != is_rejected_tester (addr, addr_len))
133 return NULL;
134 return &nonnullpointer;
135}
136
137/**
138 * Select has been forced to close a connection.
139 * Free the associated context.
140 */
141static void
142select_close_handler (void *ch_cls,
143 struct GNUNET_SelectHandle *sh,
144 struct GNUNET_SocketHandle *sock, void *sock_ctx)
145{
146 /* do nothing */
147}
148
149/**
150 * Establish a connection to a remote node.
151 *
152 * @param hello the hello-Message for the target node
153 * @param tsessionPtr the session handle that is to be set
154 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
155 */
156static int
157udp_connect (const GNUNET_MessageHello * hello,
158 GNUNET_TSession ** tsessionPtr, int may_reuse)
159{
160 GNUNET_TSession *tsession;
161
162 tsession = GNUNET_malloc (sizeof (GNUNET_TSession));
163 memset (tsession, 0, sizeof (GNUNET_TSession));
164 tsession->internal = GNUNET_malloc (GNUNET_sizeof_hello (hello));
165 memcpy (tsession->internal, hello, GNUNET_sizeof_hello (hello));
166 tsession->ttype = myAPI.protocol_number;
167 tsession->peer = hello->senderIdentity;
168 *tsessionPtr = tsession;
169 if (stats != NULL)
170 stats->change (stat_udpConnected, 1);
171 return GNUNET_OK;
172}
173
174/**
175 * A (core) Session is to be associated with a transport session. The
176 * transport service may want to know in order to call back on the
177 * core if the connection is being closed.
178 *
179 * @param tsession the session handle passed along
180 * from the call to receive that was made by the transport
181 * layer
182 * @return GNUNET_OK if the session could be associated,
183 * GNUNET_SYSERR if not.
184 */
185int
186udp_associate (GNUNET_TSession * tsession)
187{
188 return GNUNET_SYSERR; /* UDP connections can never be associated */
189}
190
191/**
192 * Disconnect from a remote node.
193 *
194 * @param tsession the session that is closed
195 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
196 */
197static int
198udp_disconnect (GNUNET_TSession * tsession)
199{
200 if (tsession != NULL)
201 {
202 if (tsession->internal != NULL)
203 GNUNET_free (tsession->internal);
204 GNUNET_free (tsession);
205 if (stats != NULL)
206 stats->change (stat_udpConnected, -1);
207 }
208 return GNUNET_OK;
209}
210
211/**
212 * Shutdown the server process (stop receiving inbound traffic). Maybe
213 * restarted later!
214 */
215static int
216udp_transport_server_stop ()
217{
218 GNUNET_GE_ASSERT (coreAPI->ectx, udp_sock != NULL);
219 if (selector != NULL)
220 {
221 GNUNET_select_destroy (selector);
222 selector = NULL;
223 }
224 GNUNET_socket_destroy (udp_sock);
225 udp_sock = NULL;
226 return GNUNET_OK;
227}
228
229/**
230 * Test if the transport would even try to send
231 * a message of the given size and importance
232 * for the given session.<br>
233 * This function is used to check if the core should
234 * even bother to construct (and encrypt) this kind
235 * of message.
236 *
237 * @return GNUNET_YES if the transport would try (i.e. queue
238 * the message or call the OS to send),
239 * GNUNET_NO if the transport would just drop the message,
240 * GNUNET_SYSERR if the size/session is invalid
241 */
242static int
243udp_test_would_try (GNUNET_TSession * tsession, unsigned int size,
244 int important)
245{
246 const GNUNET_MessageHello *hello;
247
248 if (udp_sock == NULL)
249 return GNUNET_SYSERR;
250 if (size == 0)
251 {
252 GNUNET_GE_BREAK (coreAPI->ectx, 0);
253 return GNUNET_SYSERR;
254 }
255 if (size > myAPI.mtu)
256 {
257 GNUNET_GE_BREAK (coreAPI->ectx, 0);
258 return GNUNET_SYSERR;
259 }
260 hello = (const GNUNET_MessageHello *) tsession->internal;
261 if (hello == NULL)
262 return GNUNET_SYSERR;
263 return GNUNET_YES;
264}
265
266/**
267 * Create a UDP socket. If possible, use IPv6, otherwise
268 * try IPv4. Update available_protocols accordingly.
269 */
270static int
271udp_create_socket ()
272{
273 int s;
274
275 available_protocols = VERSION_AVAILABLE_NONE;
276 s = -1;
277 if (GNUNET_YES !=
278 GNUNET_GC_get_configuration_value_yesno (cfg, "GNUNETD", "DISABLE-IPV6",
279 GNUNET_YES))
280 {
281#ifndef MINGW
282 s = SOCKET (PF_INET6, SOCK_DGRAM, 17);
283#else
284 s = win_ols_socket (PF_INET6, SOCK_DGRAM, 17);
285#endif
286 }
287 if (s < 0)
288 {
289#ifndef MINGW
290 s = SOCKET (PF_INET, SOCK_DGRAM, 17);
291#else
292 s = win_ols_socket (PF_INET, SOCK_DGRAM, 17);
293#endif
294 if (s < 0)
295 {
296 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
297 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
298 GNUNET_GE_BULK, "socket");
299 return GNUNET_SYSERR;
300 }
301 available_protocols = VERSION_AVAILABLE_IPV4;
302 }
303 else
304 {
305 available_protocols = VERSION_AVAILABLE_IPV6 | VERSION_AVAILABLE_IPV4;
306 }
307 return s;
308}
309
310/**
311 * Send a message to the specified remote node.
312 *
313 * @param tsession the GNUNET_MessageHello identifying the remote node
314 * @param message what to send
315 * @param size the size of the message
316 * @return GNUNET_SYSERR on error, GNUNET_OK on success
317 */
318static int
319udp_send (GNUNET_TSession * tsession,
320 const void *message, const unsigned int size, int important)
321{
322 const GNUNET_MessageHello *hello;
323 const HostAddress *haddr;
324 UDPMessage *mp;
325 struct sockaddr_in serverAddrv4;
326 struct sockaddr_in6 serverAddrv6;
327 struct sockaddr *serverAddr;
328 socklen_t addrlen;
329 unsigned short available;
330 int ok;
331 int ssize;
332 size_t sent;
333
334 GNUNET_GE_ASSERT (NULL, tsession != NULL);
335 if (udp_sock == NULL)
336 return GNUNET_SYSERR;
337 if (size == 0)
338 {
339 GNUNET_GE_BREAK (coreAPI->ectx, 0);
340 return GNUNET_SYSERR;
341 }
342 if (size > myAPI.mtu)
343 {
344 GNUNET_GE_BREAK (coreAPI->ectx, 0);
345 return GNUNET_SYSERR;
346 }
347 hello = (const GNUNET_MessageHello *) tsession->internal;
348 if (hello == NULL)
349 return GNUNET_SYSERR;
350
351 haddr = (const HostAddress *) &hello[1];
352 available = ntohs (haddr->availability) & available_protocols;
353 if (available == VERSION_AVAILABLE_NONE)
354 return GNUNET_SYSERR;
355 if (available == (VERSION_AVAILABLE_IPV4 | VERSION_AVAILABLE_IPV6))
356 {
357 if (GNUNET_random_u32 (GNUNET_RANDOM_QUALITY_WEAK, 2) == 0)
358 available = VERSION_AVAILABLE_IPV4;
359 else
360 available = VERSION_AVAILABLE_IPV6;
361 }
362 ssize = size + sizeof (UDPMessage);
363 mp = GNUNET_malloc (ssize);
364 mp->header.size = htons (ssize);
365 mp->header.type = 0;
366 mp->sender = *(coreAPI->my_identity);
367 memcpy (&mp[1], message, size);
368 ok = GNUNET_SYSERR;
369
370 if ((available & VERSION_AVAILABLE_IPV4) > 0)
371 {
372 memset (&serverAddrv4, 0, sizeof (serverAddrv4));
373 serverAddrv4.sin_family = AF_INET;
374 serverAddrv4.sin_port = haddr->port;
375 memcpy (&serverAddrv4.sin_addr, &haddr->ipv4, sizeof (struct in_addr));
376 addrlen = sizeof (serverAddrv4);
377 serverAddr = (struct sockaddr *) &serverAddrv4;
378 }
379 else
380 {
381 memset (&serverAddrv6, 0, 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;
388 }
389#ifndef MINGW
390 if (GNUNET_YES == GNUNET_socket_send_to (udp_sock,
391 GNUNET_NC_NONBLOCKING,
392 mp,
393 ssize, &sent,
394 (const char *) serverAddr,
395 addrlen))
396#else
397 sent =
398 win_ols_sendto (udp_sock, mp, ssize, (const char *) serverAddr, addrlen);
399 if (sent != SOCKET_ERROR)
400#endif
401 {
402 ok = GNUNET_OK;
403 if (stats != NULL)
404 stats->change (stat_bytesSent, sent);
405 }
406 else
407 {
408 if (stats != NULL)
409 stats->change (stat_bytesDropped, ssize);
410 }
411 GNUNET_free (mp);
412 return ok;
413}
414
415/**
416 * Start the server process to receive inbound traffic.
417 *
418 * @return GNUNET_OK on success, GNUNET_SYSERR if the operation failed
419 */
420static int
421udp_transport_server_start ()
422{
423 struct sockaddr_in serverAddrv4;
424 struct sockaddr_in6 serverAddrv6;
425 struct sockaddr *serverAddr;
426 socklen_t addrlen;
427 int sock;
428 const int on = 1;
429 unsigned short port;
430
431 GNUNET_GE_ASSERT (coreAPI->ectx, selector == NULL);
432 /* initialize UDP network */
433 port = get_port ();
434 if (port != 0)
435 {
436 sock = udp_create_socket ();
437 if (sock < 0)
438 return GNUNET_SYSERR;
439 if (SETSOCKOPT (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) < 0)
440 {
441 GNUNET_GE_DIE_STRERROR (coreAPI->ectx,
442 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
443 GNUNET_GE_IMMEDIATE, "setsockopt");
444 return GNUNET_SYSERR;
445 }
446 if (available_protocols == VERSION_AVAILABLE_IPV4)
447 {
448 memset (&serverAddrv4, 0, sizeof (serverAddrv4));
449 serverAddrv4.sin_family = AF_INET;
450 serverAddrv4.sin_addr.s_addr = INADDR_ANY;
451 serverAddrv4.sin_port = htons (port);
452 addrlen = sizeof (serverAddrv4);
453 serverAddr = (struct sockaddr *) &serverAddrv4;
454 }
455 else
456 {
457 memset (&serverAddrv6, 0, sizeof (serverAddrv6));
458 serverAddrv6.sin6_family = AF_INET6;
459 serverAddrv6.sin6_addr = in6addr_any;
460 serverAddrv6.sin6_port = htons (port);
461 addrlen = sizeof (serverAddrv6);
462 serverAddr = (struct sockaddr *) &serverAddrv6;
463 }
464 if (BIND (sock, serverAddr, addrlen) < 0)
465 {
466 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
467 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
468 GNUNET_GE_IMMEDIATE, "bind");
469 GNUNET_GE_LOG (coreAPI->ectx,
470 GNUNET_GE_FATAL | GNUNET_GE_ADMIN |
471 GNUNET_GE_IMMEDIATE,
472 _("Failed to bind to %s port %d.\n"),
473 MY_TRANSPORT_NAME, port);
474 if (0 != CLOSE (sock))
475 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
476 GNUNET_GE_ERROR | GNUNET_GE_USER |
477 GNUNET_GE_ADMIN | GNUNET_GE_BULK,
478 "close");
479 return GNUNET_SYSERR;
480 }
481 selector = GNUNET_select_create ("udp", GNUNET_YES, coreAPI->ectx, load_monitor, sock, addrlen, 0, /* timeout */
482 &select_message_handler,
483 NULL,
484 &select_accept_handler,
485 NULL,
486 &select_close_handler,
487 NULL, 64 * 1024,
488 16 /* max sockets */ );
489 if (selector == NULL)
490 return GNUNET_SYSERR;
491 }
492 sock = udp_create_socket ();
493 if (sock == -1)
494 {
495 GNUNET_GE_LOG_STRERROR (coreAPI->ectx,
496 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
497 GNUNET_GE_BULK, "socket");
498 GNUNET_select_destroy (selector);
499 selector = NULL;
500 return GNUNET_SYSERR;
501 }
502 udp_sock = GNUNET_socket_create (coreAPI->ectx, load_monitor, sock);
503 GNUNET_GE_ASSERT (coreAPI->ectx, udp_sock != NULL);
504 return GNUNET_OK;
505}
506
507/**
508 * The exported method. Makes the core api available via a global and
509 * returns the udp transport API.
510 */
511GNUNET_TransportAPI *
512inittransport_udp (GNUNET_CoreAPIForTransport * core)
513{
514 unsigned long long mtu;
515
516 cfg = core->cfg;
517 load_monitor = core->load_monitor;
518 GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (UDPMessage) == 68);
519 GNUNET_GE_ASSERT (coreAPI->ectx, sizeof (HostAddress) == 24);
520 coreAPI = core;
521 if (-1 == GNUNET_GC_get_configuration_value_number (cfg,
522 "UDP",
523 "MTU",
524 sizeof (UDPMessage)
525 +
526 GNUNET_P2P_MESSAGE_OVERHEAD
527 +
528 sizeof
529 (GNUNET_MessageHeader) +
530 32, 65500,
531 MESSAGE_SIZE, &mtu))
532 {
533 return NULL;
534 }
535 if (mtu < 1200)
536 GNUNET_GE_LOG (coreAPI->ectx,
537 GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE,
538 _("MTU %llu for `%s' is probably too low!\n"), mtu, "UDP");
539 lock = GNUNET_mutex_create (GNUNET_NO);
540 if (0 !=
541 GNUNET_GC_attach_change_listener (cfg, &reload_configuration, NULL))
542 {
543 GNUNET_mutex_destroy (lock);
544 lock = NULL;
545 return NULL;
546 }
547 if (GNUNET_GC_get_configuration_value_yesno (cfg, "UDP", "UPNP", GNUNET_YES)
548 == GNUNET_YES)
549 {
550 upnp = coreAPI->service_request ("upnp");
551 if (upnp == NULL)
552 GNUNET_GE_LOG (coreAPI->ectx,
553 GNUNET_GE_ERROR | GNUNET_GE_USER | GNUNET_GE_IMMEDIATE,
554 "The UPnP service could not be loaded. To disable UPnP, set the "
555 "configuration option \"UPNP\" in section \"%s\" to \"NO\"\n",
556 "UDP");
557 }
558 stats = coreAPI->service_request ("stats");
559 if (stats != NULL)
560 {
561 stat_bytesReceived
562 = stats->create (gettext_noop ("# bytes received via UDP"));
563 stat_bytesSent = stats->create (gettext_noop ("# bytes sent via UDP"));
564 stat_bytesDropped
565 = stats->create (gettext_noop ("# bytes dropped by UDP (outgoing)"));
566 stat_udpConnected
567 = stats->create (gettext_noop ("# UDP connections (right now)"));
568 }
569 myAPI.protocol_number = GNUNET_TRANSPORT_PROTOCOL_NUMBER_UDP;
570 myAPI.mtu = mtu - sizeof (UDPMessage);
571 myAPI.cost = 20000;
572 myAPI.hello_verify = &verify_hello;
573 myAPI.hello_create = &create_hello;
574 myAPI.connect = &udp_connect;
575 myAPI.send = &udp_send;
576 myAPI.associate = &udp_associate;
577 myAPI.disconnect = &udp_disconnect;
578 myAPI.server_start = &udp_transport_server_start;
579 myAPI.server_stop = &udp_transport_server_stop;
580 myAPI.hello_to_address = &hello_to_address;
581 myAPI.send_now_test = &udp_test_would_try;
582
583 return &myAPI;
584}
585
586void
587donetransport_udp ()
588{
589 do_shutdown ();
590}
591
592/* end of udp.c */