X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftransport%2Fgnunet-communicator-udp.c;h=fa8eb6acb913e602e45cf58158795abfccc29ff6;hb=9859b3162a9fd7c9cc0c19582b04dd7f1c1f9408;hp=bbfe2ebec38bdeb5a8f5c37b5044e0c8032cb693;hpb=31f4c60c63dbd3958da7aac32f81cb20770eab97;p=oweals%2Fgnunet.git diff --git a/src/transport/gnunet-communicator-udp.c b/src/transport/gnunet-communicator-udp.c index bbfe2ebec..fa8eb6acb 100644 --- a/src/transport/gnunet-communicator-udp.c +++ b/src/transport/gnunet-communicator-udp.c @@ -24,17 +24,13 @@ * @author Christian Grothoff * * TODO: - * - implement main BOXed sending logic - * - figure out what to do with MTU: 1280 for IPv6 is obvious; - * what for IPv4? 1500? Also, consider differences in - * headers for with/without box: need to give MIN of both - * to TNG (as TNG expects a fixed MTU!), or maybe - * we create a FRESH MQ while we have available BOXes SQNs? - * (otherwise padding will REALLY hurt) - * - add and use util/ check for IPv6 availability (#V6) * - consider imposing transmission limits in the absence * of ACKs; or: maybe this should be done at TNG service level? - * - handle addresses discovered fro broadcasts (#) + * (at least the receiver might want to enforce limits on + * KX/DH operations per sender in here) (#5552) + * - overall, we should look more into flow control support + * (either in backchannel, or general solution in TNG service) + * - handle addresses discovered from broadcasts (#5551) * (think: what was the story again on address validation? * where is the API for that!?!) * - support DNS names in BINDTO option (#5528) @@ -535,6 +531,12 @@ struct ReceiverAddress */ struct SharedSecret *ss_tail; + /** + * Address of the receiver in the human-readable format + * with the #COMMUNICATOR_ADDRESS_PREFIX. + */ + char *foreign_addr; + /** * Address of the other peer. */ @@ -574,6 +576,12 @@ struct ReceiverAddress * Length of the DLL at @a ss_head. */ unsigned int num_secrets; + + /** + * Number of BOX keys from ACKs we have currently + * available for this receiver. + */ + unsigned int acks_available; /** * Which network type does this queue use? @@ -789,6 +797,11 @@ receiver_destroy (struct ReceiverAddress *receiver) receiver->mq = NULL; GNUNET_MQ_destroy (mq); } + if (NULL != receiver->qh) + { + GNUNET_TRANSPORT_communicator_mq_del (receiver->qh); + receiver->qh = NULL; + } GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multipeermap_remove (receivers, &receiver->target, @@ -800,6 +813,7 @@ receiver_destroy (struct ReceiverAddress *receiver) GNUNET_CONTAINER_multipeermap_size (receivers), GNUNET_NO); GNUNET_free (receiver->address); + GNUNET_free (receiver->foreign_addr); GNUNET_free (receiver); } @@ -913,6 +927,8 @@ secret_destroy (struct SharedSecret *ss) receiver->ss_tail, ss); receiver->num_secrets--; + receiver->acks_available + -= (ss->sequence_allowed - ss->sequence_used); } while (NULL != (kce = ss->kce_head)) kce_destroy (kce); @@ -1252,6 +1268,17 @@ setup_shared_secret_enc (const struct GNUNET_CRYPTO_EcdhePrivateKey *ephemeral, } +/** + * Setup the MQ for the @a receiver. If a queue exists, + * the existing one is destroyed. Then the MTU is + * recalculated and a fresh queue is initialized. + * + * @param receiver receiver to setup MQ for + */ +static void +setup_receiver_mq (struct ReceiverAddress *receiver); + + /** * We received an ACK for @a pid. Check if it is for * the receiver in @a value and if so, handle it and @@ -1279,17 +1306,28 @@ handle_ack (void *cls, &ss->cmac, sizeof (struct GNUNET_HashCode))) { - ss->sequence_allowed = GNUNET_MAX (ss->sequence_allowed, - ntohl (ack->sequence_max)); - /* move ss to head to avoid discarding it anytime soon! */ - GNUNET_CONTAINER_DLL_remove (receiver->ss_head, - receiver->ss_tail, - ss); - GNUNET_CONTAINER_DLL_insert (receiver->ss_head, - receiver->ss_tail, - ss); - /* FIXME: if this changed sequence_allowed, - update MTU / MQ of 'receiver'! */ + uint32_t allowed; + + allowed = ntohl (ack->sequence_max); + + if (allowed > ss->sequence_allowed) + { + receiver->acks_available += (allowed - ss->sequence_allowed); + if ((allowed - ss->sequence_allowed) + == receiver->acks_available) + { + /* we just incremented from zero => MTU change! */ + setup_receiver_mq (receiver); + } + ss->sequence_allowed = allowed; + /* move ss to head to avoid discarding it anytime soon! */ + GNUNET_CONTAINER_DLL_remove (receiver->ss_head, + receiver->ss_tail, + ss); + GNUNET_CONTAINER_DLL_insert (receiver->ss_head, + receiver->ss_tail, + ss); + } return GNUNET_NO; } } @@ -1633,7 +1671,7 @@ sock_read (void *cls) "# broadcasts received", 1, GNUNET_NO); - // FIXME: we effectively just got a HELLO! + // FIXME #5551: we effectively just got a HELLO! // trigger verification NOW! return; } @@ -1745,12 +1783,12 @@ udp_address_to_sockaddr (const char *bindto, bindto); return NULL; } - /* FIXME #V6: add test to util/ for IPv6 availability, - and depending on the result, go directly for v4-only */ - if (GNUNET_YES == - GNUNET_CONFIGURATION_get_value_yesno (cfg, - COMMUNICATOR_CONFIG_SECTION, - "DISABLE_V6")) + if ( (GNUNET_NO == + GNUNET_NETWORK_test_pf (PF_INET6)) || + (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + COMMUNICATOR_CONFIG_SECTION, + "DISABLE_V6")) ) { struct sockaddr_in *i4; @@ -1854,6 +1892,43 @@ udp_address_to_sockaddr (const char *bindto, } +/** + * Pad @a dgram by @a pad_size using @a out_cipher. + * + * @param out_cipher cipher to use + * @param dgram datagram to pad + * @param pad_size number of bytes of padding to append + */ +static void +do_pad (gcry_cipher_hd_t out_cipher, + char *dgram, + size_t pad_size) +{ + char pad[pad_size]; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + pad, + sizeof (pad)); + if (sizeof (pad) > sizeof (struct GNUNET_MessageHeader)) + { + struct GNUNET_MessageHeader hdr = { + .size = htons (sizeof (pad)), + .type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_PAD) + }; + + memcpy (pad, + &hdr, + sizeof (hdr)); + } + GNUNET_assert (0 == + gcry_cipher_encrypt (out_cipher, + dgram, + sizeof (pad), + pad, + sizeof (pad))); +} + + /** * Signature of functions implementing the sending functionality of a * message queue. @@ -1879,10 +1954,9 @@ mq_send (struct GNUNET_MQ_Handle *mq, } reschedule_receiver_timeout (receiver); - // FIXME: add support for BOX encryption method! - - /* KX encryption method */ + if (0 == receiver->acks_available) { + /* use KX encryption method */ struct UdpHandshakeSignature uhs; struct UDPConfirmation uc; struct InitialKX kx; @@ -1935,31 +2009,9 @@ mq_send (struct GNUNET_MQ_Handle *mq, msg, msize)); dpos += msize; - /* Pad to MTU */ - { - char pad[sizeof (dgram) - dpos]; - - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - pad, - sizeof (pad)); - if (sizeof (pad) > sizeof (struct GNUNET_MessageHeader)) - { - struct GNUNET_MessageHeader hdr = { - .size = htons (sizeof (pad)), - .type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_PAD) - }; - - memcpy (pad, - &hdr, - sizeof (hdr)); - GNUNET_assert (0 == - gcry_cipher_encrypt (out_cipher, - &dgram[dpos], - sizeof (pad), - pad, - sizeof (pad))); - } - } + do_pad (out_cipher, + &dgram[dpos], + sizeof (dgram) - dpos); /* Datagram starts with kx */ kx.ephemeral = uhs.ephemeral; GNUNET_assert (0 == @@ -1979,7 +2031,65 @@ mq_send (struct GNUNET_MQ_Handle *mq, GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send"); GNUNET_MQ_impl_send_continue (mq); + return; } /* End of KX encryption method */ + + /* begin "BOX" encryption method, scan for ACKs from tail! */ + for (struct SharedSecret *ss = receiver->ss_tail; + NULL != ss; + ss = ss->prev) + { + if (ss->sequence_used < ss->sequence_allowed) + { + char dgram[sizeof (struct UDPBox) + receiver->mtu]; + struct UDPBox *box; + gcry_cipher_hd_t out_cipher; + size_t dpos; + + box = (struct UDPBox *) dgram; + ss->sequence_used++; + get_kid (&ss->master, + ss->sequence_used, + &box->kid); + setup_cipher (&ss->master, + ss->sequence_used, + &out_cipher); + /* Append encrypted payload to dgram */ + dpos = sizeof (struct UDPBox); + GNUNET_assert (0 == + gcry_cipher_encrypt (out_cipher, + &dgram[dpos], + msize, + msg, + msize)); + dpos += msize; + do_pad (out_cipher, + &dgram[dpos], + sizeof (dgram) - dpos); + GNUNET_assert (0 == + gcry_cipher_gettag (out_cipher, + box->gcm_tag, + sizeof (box->gcm_tag))); + gcry_cipher_close (out_cipher); + if (-1 == + GNUNET_NETWORK_socket_sendto (udp_sock, + dgram, + sizeof (dgram), + receiver->address, + receiver->address_len)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "send"); + GNUNET_MQ_impl_send_continue (mq); + receiver->acks_available--; + if (0 == receiver->acks_available) + { + /* We have no more ACKs => MTU change! */ + setup_receiver_mq (receiver); + } + return; + } + } + GNUNET_assert (0); } @@ -2043,6 +2153,77 @@ mq_error (void *cls, } +/** + * Setup the MQ for the @a receiver. If a queue exists, + * the existing one is destroyed. Then the MTU is + * recalculated and a fresh queue is initialized. + * + * @param receiver receiver to setup MQ for + */ +static void +setup_receiver_mq (struct ReceiverAddress *receiver) +{ + size_t base_mtu; + + if (NULL != receiver->qh) + { + GNUNET_TRANSPORT_communicator_mq_del (receiver->qh); + receiver->qh = NULL; + } + GNUNET_assert (NULL == receiver->mq); + switch (receiver->address->sa_family) + { + case AF_INET: + base_mtu + = 1480 /* Ethernet MTU, 1500 - Ethernet header - VLAN tag */ + - sizeof (struct GNUNET_TUN_IPv4Header) /* 20 */ + - sizeof (struct GNUNET_TUN_UdpHeader) /* 8 */; + break; + case AF_INET6: + base_mtu + = 1280 /* Minimum MTU required by IPv6 */ + - sizeof (struct GNUNET_TUN_IPv6Header) /* 40 */ + - sizeof (struct GNUNET_TUN_UdpHeader) /* 8 */; + break; + default: + GNUNET_assert (0); + break; + } + if (0 == receiver->acks_available) + { + /* MTU based on full KX messages */ + receiver->mtu + = base_mtu + - sizeof (struct InitialKX) /* 48 */ + - sizeof (struct UDPConfirmation); /* 104 */ + } + else + { + /* MTU based on BOXed messages */ + receiver->mtu + = base_mtu - sizeof (struct UDPBox); + } + /* => Effective MTU for CORE will range from 1080 (IPv6 + KX) to + 1404 (IPv4 + Box) bytes, depending on circumstances... */ + receiver->mq + = GNUNET_MQ_queue_for_callbacks (&mq_send, + &mq_destroy, + &mq_cancel, + receiver, + NULL, + &mq_error, + receiver); + receiver->qh + = GNUNET_TRANSPORT_communicator_mq_add (ch, + &receiver->target, + receiver->foreign_addr, + receiver->mtu, + receiver->nt, + GNUNET_TRANSPORT_CS_OUTBOUND, + receiver->mq); +} + + /** * Setup a receiver for transmission. Setup the MQ processing and * inform transport that the queue is ready. @@ -2073,54 +2254,35 @@ receiver_setup (const struct GNUNET_PeerIdentity *target, receiver->hn = GNUNET_CONTAINER_heap_insert (receivers_heap, receiver, receiver->timeout.abs_value_us); - receiver->mq - = GNUNET_MQ_queue_for_callbacks (&mq_send, - &mq_destroy, - &mq_cancel, - receiver, - NULL, - &mq_error, - receiver); - receiver->mtu = 1200 /* FIXME: MTU OK? */; - if (NULL == timeout_task) - timeout_task = GNUNET_SCHEDULER_add_now (&check_timeouts, - NULL); GNUNET_STATISTICS_set (stats, "# receivers active", GNUNET_CONTAINER_multipeermap_size (receivers), GNUNET_NO); + switch (address->sa_family) { - char *foreign_addr; - - switch (address->sa_family) - { - case AF_INET: - GNUNET_asprintf (&foreign_addr, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_a2s (receiver->address, - receiver->address_len)); - break; - case AF_INET6: - GNUNET_asprintf (&foreign_addr, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_a2s (receiver->address, - receiver->address_len)); - break; - default: - GNUNET_assert (0); - } - receiver->qh - = GNUNET_TRANSPORT_communicator_mq_add (ch, - &receiver->target, - foreign_addr, - receiver->mtu, - receiver->nt, - GNUNET_TRANSPORT_CS_OUTBOUND, - receiver->mq); - GNUNET_free (foreign_addr); + case AF_INET: + GNUNET_asprintf (&receiver->foreign_addr, + "%s-%s", + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_a2s (receiver->address, + receiver->address_len)); + break; + case AF_INET6: + GNUNET_asprintf (&receiver->foreign_addr, + "%s-%s", + COMMUNICATOR_ADDRESS_PREFIX, + GNUNET_a2s (receiver->address, + receiver->address_len)); + break; + default: + GNUNET_assert (0); } + + setup_receiver_mq (receiver); + + if (NULL == timeout_task) + timeout_task = GNUNET_SCHEDULER_add_now (&check_timeouts, + NULL); return receiver; }