X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftransport%2Fgnunet-communicator-udp.c;h=fa8eb6acb913e602e45cf58158795abfccc29ff6;hb=9859b3162a9fd7c9cc0c19582b04dd7f1c1f9408;hp=f307f6052b2fa7d983c9b4f4cc7823dc01717d26;hpb=ea7d98e90d841200c2eb8cee31910b5bfa484880;p=oweals%2Fgnunet.git diff --git a/src/transport/gnunet-communicator-udp.c b/src/transport/gnunet-communicator-udp.c index f307f6052..fa8eb6acb 100644 --- a/src/transport/gnunet-communicator-udp.c +++ b/src/transport/gnunet-communicator-udp.c @@ -24,16 +24,18 @@ * @author Christian Grothoff * * TODO: - * - 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) + * - consider imposing transmission limits in the absence + * of ACKs; or: maybe this should be done at TNG service level? + * (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) * - support NAT connection reversal method (#5529) - * - support other UDP-specific NAT traversal methods + * - support other UDP-specific NAT traversal methods (#) */ #include "platform.h" #include "gnunet_util_lib.h" @@ -45,16 +47,6 @@ #include "gnunet_statistics_service.h" #include "gnunet_transport_communication_service.h" -/** - * How many messages do we keep at most in the queue to the - * transport service before we start to drop (default, - * can be changed via the configuration file). - * Should be _below_ the level of the communicator API, as - * otherwise we may read messages just to have them dropped - * by the communicator API. - */ -#define DEFAULT_MAX_QUEUE_LENGTH 8 - /** * How often do we rekey based on time (at least) */ @@ -65,6 +57,31 @@ */ #define PROTO_QUEUE_TIMEOUT GNUNET_TIME_UNIT_MINUTES +/** + * How often do we broadcast our presence on the LAN? + */ +#define BROADCAST_FREQUENCY GNUNET_TIME_UNIT_MINUTES + +/** + * How often do we scan for changes to our network interfaces? + */ +#define INTERFACE_SCAN_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) + +/** + * AES key size. + */ +#define AES_KEY_SIZE (256/8) + +/** + * AES (GCM) IV size. + */ +#define AES_IV_SIZE (96/8) + +/** + * Size of the GCM tag. + */ +#define GCM_TAG_SIZE (128/8) + /** * If we fall below this number of available KCNs, * we generate additional ACKs until we reach @@ -95,6 +112,17 @@ */ #define MAX_SQN_DELTA 160 +/** + * How many shared master secrets do we keep around + * at most per sender? Should be large enough so + * that we generally have a chance of sending an ACK + * before the sender already rotated out the master + * secret. Generally values around #KCN_TARGET make + * sense. Might make sense to adapt to RTT if we had + * a good measurement... + */ +#define MAX_SECRETS 128 + /** * How often do we rekey based on number of bytes transmitted? * (additionally randomized). @@ -104,6 +132,7 @@ /** * Address prefix used by the communicator. */ + #define COMMUNICATOR_ADDRESS_PREFIX "udp" /** @@ -159,13 +188,13 @@ struct InitialKX /** * Ephemeral key for KX. */ - struct GNUNET_CRYPT_EddsaPublicKey ephemeral; + struct GNUNET_CRYPTO_EcdhePublicKey ephemeral; /** * HMAC for the following encrypted message, using GCM. HMAC uses * key derived from the handshake with sequence number zero. */ - uint8_t gcm_tag[128/8]; + char gcm_tag[GCM_TAG_SIZE]; }; @@ -224,6 +253,56 @@ struct UDPAck }; +/** + * Signature we use to verify that the broadcast was really made by + * the peer that claims to have made it. Basically, affirms that the + * peer is really using this IP address (albeit possibly not in _our_ + * LAN). Makes it difficult for peers in the LAN to claim to + * be just any global peer -- an attacker must have at least + * shared a LAN with the peer they're pretending to be here. + */ +struct UdpBroadcastSignature +{ + /** + * Purpose must be #GNUNET_SIGNATURE_COMMUNICATOR_UDP_BROADCAST + */ + struct GNUNET_CRYPTO_EccSignaturePurpose purpose; + + /** + * Identity of the inititor of the UDP broadcast. + */ + struct GNUNET_PeerIdentity sender; + + /** + * Hash of the sender's UDP address. + */ + struct GNUNET_HashCode h_address; +}; + + +/** + * Broadcast by peer in LAN announcing its presence. Unusual in that + * we don't pad these to full MTU, as we cannot prevent being + * recognized in LAN as GNUnet peers if this feature is enabled + * anyway. Also, the entire message is in cleartext. + */ +struct UDPBroadcast +{ + + /** + * Sender's peer identity. + */ + struct GNUNET_PeerIdentity sender; + + /** + * Sender's signature of type + * #GNUNET_SIGNATURE_COMMUNICATOR_UDP_BROADCAST + */ + struct GNUNET_CRYPTO_EddsaSignature sender_sig; + +}; + + /** * UDP message box. Always sent encrypted, only allowed after * the receiver sent a `struct UDPAck` for the base key! @@ -247,8 +326,7 @@ struct UDPBox * wrong, the receiver should check if the message might be a * `struct UdpHandshakeSignature`. */ - uint8_t gcm_tag[128/8]; - + char gcm_tag[GCM_TAG_SIZE]; }; @@ -358,7 +436,7 @@ struct SharedSecret /** * Up to which sequence number did we use this @e master already? - * (for sending or receiving) + * (for encrypting only) */ uint32_t sequence_used; @@ -453,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. */ @@ -463,6 +547,11 @@ struct ReceiverAddress */ socklen_t address_len; + /** + * Entry in sender expiration heap. + */ + struct GNUNET_CONTAINER_HeapNode *hn; + /** * Message queue we are providing for the #ch. */ @@ -478,10 +567,21 @@ struct ReceiverAddress */ struct GNUNET_TIME_Absolute timeout; + /** + * MTU we allowed transport for this receiver right now. + */ + size_t mtu; + /** * 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? @@ -491,10 +591,64 @@ struct ReceiverAddress }; +/** + * Interface we broadcast our presence on. + */ +struct BroadcastInterface +{ + + /** + * Kept in a DLL. + */ + struct BroadcastInterface *next; + + /** + * Kept in a DLL. + */ + struct BroadcastInterface *prev; + + /** + * Task for this broadcast interface. + */ + struct GNUNET_SCHEDULER_Task *broadcast_task; + + /** + * Sender's address of the interface. + */ + struct sockaddr *sa; + + /** + * Broadcast address to use on the interface. + */ + struct sockaddr *ba; + + /** + * Message we broadcast on this interface. + */ + struct UDPBroadcast bcm; + + /** + * If this is an IPv6 interface, this is the request + * we use to join/leave the group. + */ + struct ipv6_mreq mcreq; + + /** + * Number of bytes in @e sa. + */ + socklen_t salen; + + /** + * Was this interface found in the last #iface_proc() scan? + */ + int found; +}; + + /** * Cache of pre-generated key IDs. */ -static struct GNUNET_CONTINER_MultiShortMap *key_cache; +static struct GNUNET_CONTAINER_MultiShortmap *key_cache; /** * ID of read task @@ -506,6 +660,11 @@ static struct GNUNET_SCHEDULER_Task *read_task; */ static struct GNUNET_SCHEDULER_Task *timeout_task; +/** + * ID of master broadcast task + */ +static struct GNUNET_SCHEDULER_Task *broadcast_task; + /** * For logging statistics. */ @@ -536,11 +695,26 @@ static struct GNUNET_CONTAINER_Heap *senders_heap; */ static struct GNUNET_CONTAINER_Heap *receivers_heap; +/** + * Broadcast interface tasks. Kept in a DLL. + */ +static struct BroadcastInterface *bi_head; + +/** + * Broadcast interface tasks. Kept in a DLL. + */ +static struct BroadcastInterface *bi_tail; + /** * Our socket. */ static struct GNUNET_NETWORK_Handle *udp_sock; +/** + * #GNUNET_YES if #udp_sock supports IPv6. + */ +static int have_v6_socket; + /** * Our public key. */ @@ -566,10 +740,47 @@ static struct GNUNET_NT_InterfaceScanner *is; */ static struct GNUNET_NAT_Handle *nat; +/** + * Port number to which we are actually bound. + */ +static uint16_t my_port; + /** - * Functions with this signature are called whenever we need - * to close a receiving state due to timeout. + * An interface went away, stop broadcasting on it. + * + * @param bi entity to close down + */ +static void +bi_destroy (struct BroadcastInterface *bi) +{ + if (AF_INET6 == bi->sa->sa_family) + { + /* Leave the multicast group */ + if (GNUNET_OK != + GNUNET_NETWORK_socket_setsockopt + (udp_sock, + IPPROTO_IPV6, + IPV6_LEAVE_GROUP, + &bi->mcreq, + sizeof (bi->mcreq))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "setsockopt"); + } + } + GNUNET_CONTAINER_DLL_remove (bi_head, + bi_tail, + bi); + GNUNET_SCHEDULER_cancel (bi->broadcast_task); + GNUNET_free (bi->sa); + GNUNET_free_non_null (bi->ba); + GNUNET_free (bi); +} + + +/** + * Destroys a receiving state due to timeout or shutdown. * * @param receiver entity to close down */ @@ -586,18 +797,23 @@ 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, receiver)); - GNUNET_assert (sender == - GNUNET_CONTAINER_heap_remove_node (receivers_heap, - receiver->hn)); + GNUNET_assert (receiver == + GNUNET_CONTAINER_heap_remove_node (receiver->hn)); GNUNET_STATISTICS_set (stats, "# receivers active", GNUNET_CONTAINER_multipeermap_size (receivers), GNUNET_NO); GNUNET_free (receiver->address); + GNUNET_free (receiver->foreign_addr); GNUNET_free (receiver); } @@ -681,7 +897,7 @@ kce_generate (struct SharedSecret *ss, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); GNUNET_STATISTICS_set (stats, "# KIDs active", - GNUNET_CONTAINER_multipeermap_size (key_cache), + GNUNET_CONTAINER_multishortmap_size (key_cache), GNUNET_NO); } @@ -711,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); @@ -720,7 +938,7 @@ secret_destroy (struct SharedSecret *ss) GNUNET_NO); GNUNET_STATISTICS_set (stats, "# KIDs active", - GNUNET_CONTAINER_multipeermap_size (key_cache), + GNUNET_CONTAINER_multishortmap_size (key_cache), GNUNET_NO); GNUNET_free (ss); } @@ -740,8 +958,7 @@ sender_destroy (struct SenderAddress *sender) &sender->target, sender)); GNUNET_assert (sender == - GNUNET_CONTAINER_heap_remove_node (senders_heap, - sender->hn)); + GNUNET_CONTAINER_heap_remove_node (sender->hn)); GNUNET_STATISTICS_set (stats, "# senders active", GNUNET_CONTAINER_multipeermap_size (senders), @@ -751,38 +968,6 @@ sender_destroy (struct SenderAddress *sender) } -/** - * Compute @a smac over @a buf. - * - * @param msec master secret for HMAC calculation - * @param serial number for the @a smac calculation - * @param buf buffer to MAC - * @param buf_size number of bytes in @a buf - * @param smac[out] where to write the HMAC - */ -static void -get_hmac (const struct GNUNET_HashCode *msec, - uint32_t serial, - const void *buf, - size_t buf_size, - struct GNUNET_ShortHashCode *smac) -{ - uint32_t sid = htonl (serial); - - GNUNET_CRYPTO_hkdf (smac, - sizeof (*smac), - GCRY_MD_SHA512, - GCRY_MD_SHA256, - &sid, - sizeof (sid), - msec, - sizeof (*msec), - "UDP-HMAC", - strlen ("UDP-HMAC"), - NULL, 0); -} - - /** * Compute @a key and @a iv. * @@ -794,11 +979,11 @@ get_hmac (const struct GNUNET_HashCode *msec, static void get_iv_key (const struct GNUNET_HashCode *msec, uint32_t serial, - char key[256/8], - char iv[96/8]) + char key[AES_KEY_SIZE], + char iv[AES_IV_SIZE]) { uint32_t sid = htonl (serial); - char res[sizeof(key) + sizeof (iv)]; + char res[AES_KEY_SIZE + AES_IV_SIZE]; GNUNET_CRYPTO_hkdf (res, sizeof (res), @@ -812,11 +997,11 @@ get_iv_key (const struct GNUNET_HashCode *msec, strlen ("UDP-IV-KEY"), NULL, 0); memcpy (key, - sid, - sizeof (key)); + res, + AES_KEY_SIZE); memcpy (iv, - &sid[sizeof(key)], - sizeof (iv)); + &res[AES_KEY_SIZE], + AES_IV_SIZE); } @@ -831,7 +1016,7 @@ reschedule_sender_timeout (struct SenderAddress *sender) sender->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); GNUNET_CONTAINER_heap_update_cost (sender->hn, - sender.timeout.abs_value_us); + sender->timeout.abs_value_us); } @@ -846,7 +1031,7 @@ reschedule_receiver_timeout (struct ReceiverAddress *receiver) receiver->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); GNUNET_CONTAINER_heap_update_cost (receiver->hn, - receiver.timeout.abs_value_us); + receiver->timeout.abs_value_us); } @@ -935,7 +1120,7 @@ pass_plaintext_to_core (struct SenderAddress *sender, ntohs (hdr->size), GNUNET_NO); (void) GNUNET_TRANSPORT_communicator_receive (ch, - &queue->target, + &sender->target, hdr, NULL /* no flow control possible */, NULL); @@ -965,8 +1150,8 @@ setup_cipher (const struct GNUNET_HashCode *msec, uint32_t serial, gcry_cipher_hd_t *cipher) { - char key[256/8]; - char iv[96/8]; + char key[AES_KEY_SIZE]; + char iv[AES_IV_SIZE]; gcry_cipher_open (cipher, GCRY_CIPHER_AES256 /* low level: go for speed */, @@ -999,7 +1184,7 @@ setup_cipher (const struct GNUNET_HashCode *msec, */ static int try_decrypt (const struct SharedSecret *ss, - char tag[128/8], + const char tag[GCM_TAG_SIZE], uint32_t serial, const char *in_buf, size_t in_buf_size, @@ -1012,14 +1197,14 @@ try_decrypt (const struct SharedSecret *ss, &cipher); GNUNET_assert (0 == gcry_cipher_decrypt (cipher, - in_buf, - in_buf_size, out_buf, + in_buf_size, + in_buf, in_buf_size)); if (0 != gcry_cipher_checktag (cipher, tag, - sizeof (tag))) + GCM_TAG_SIZE)) { gcry_cipher_close (cipher); GNUNET_STATISTICS_update (stats, @@ -1069,7 +1254,7 @@ setup_shared_secret_enc (const struct GNUNET_CRYPTO_EcdhePrivateKey *ephemeral, GNUNET_CRYPTO_ecdh_eddsa (ephemeral, &receiver->target.public_key, &ss->master); - calculcate_cmac (ss); + calculate_cmac (ss); ss->receiver = receiver; GNUNET_CONTAINER_DLL_insert (receiver->ss_head, receiver->ss_tail, @@ -1083,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 @@ -1110,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 (sender->ss_head, - sender->ss_tail, - ss); - GNUNET_CONTAINER_DLL_insert (sender->ss_head, - sender->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; } } @@ -1142,21 +1349,21 @@ try_handle_plaintext (struct SenderAddress *sender, size_t buf_size) { const struct GNUNET_MessageHeader *hdr - = (const struct GNUNET_MessageHeader *) queue->pread_buf; + = (const struct GNUNET_MessageHeader *) buf; const struct UDPAck *ack - = (const struct UDPAck *) queue->pread_buf; + = (const struct UDPAck *) buf; uint16_t type; if (sizeof (*hdr) > buf_size) return; /* not even a header */ if (ntohs (hdr->size) > buf_size) - return 0; /* not even a header */ + return; /* not even a header */ type = ntohs (hdr->type); switch (type) { case GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_ACK: /* lookup master secret by 'cmac', then update sequence_max */ - GNUNET_CONTAINER_multihashmap_get_multiple (receivers, + GNUNET_CONTAINER_multipeermap_get_multiple (receivers, &sender->target, &handle_ack, (void *) ack); @@ -1200,10 +1407,10 @@ consider_ss_ack (struct SharedSecret *ss) while (ss->active_kce_count < KCN_TARGET) kce_generate (ss, - ++ss->sequence_used); + ++ss->sequence_allowed); ack.header.type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_ACK); ack.header.size = htons (sizeof (ack)); - ack.sequence_max = htonl (ss->sequence_max); + ack.sequence_max = htonl (ss->sequence_allowed); ack.cmac = ss->cmac; GNUNET_TRANSPORT_communicator_notify (ch, &ss->sender->target, @@ -1226,7 +1433,6 @@ decrypt_box (const struct UDPBox *box, struct KeyCacheEntry *kce) { struct SharedSecret *ss = kce->ss; - gcry_cipher_hd_t cipher; char out_buf[box_len - sizeof (*box)]; GNUNET_assert (NULL != ss->sender); @@ -1234,9 +1440,9 @@ decrypt_box (const struct UDPBox *box, try_decrypt (ss, box->gcm_tag, kce->sequence_number, - box_len - sizeof (*box), - out_buf, - sizeof (out_buf))) + (const char *) &box[1], + sizeof (out_buf), + out_buf)) { GNUNET_STATISTICS_update (stats, "# Decryption failures with valid KCE", @@ -1330,7 +1536,7 @@ setup_sender (const struct GNUNET_PeerIdentity *target, .sender = NULL }; - GNUNET_CONTAINER_multihashmap_get_multiple (senders, + GNUNET_CONTAINER_multipeermap_get_multiple (senders, target, &find_sender_by_address, &sc); @@ -1344,7 +1550,7 @@ setup_sender (const struct GNUNET_PeerIdentity *target, sender->address = GNUNET_memdup (address, address_len); sender->address_len = address_len; - (void) GNUNET_CONTAINER_multihashmap_put (senders, + (void) GNUNET_CONTAINER_multipeermap_put (senders, &sender->target, sender, GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); @@ -1356,7 +1562,7 @@ setup_sender (const struct GNUNET_PeerIdentity *target, = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); sender->hn = GNUNET_CONTAINER_heap_insert (senders_heap, sender, - sender.timeout.abs_value_us); + sender->timeout.abs_value_us); sender->nt = GNUNET_NT_scanner_get_type (is, address, address_len); @@ -1367,6 +1573,32 @@ setup_sender (const struct GNUNET_PeerIdentity *target, } +/** + * Check signature from @a uc against @a ephemeral. + * + * @param ephermal key that is signed + * @param uc signature of claimant + * @return #GNUNET_OK if signature is valid + */ +static int +verify_confirmation (const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral, + const struct UDPConfirmation *uc) +{ + struct UdpHandshakeSignature uhs; + + uhs.purpose.purpose = htonl (GNUNET_SIGNATURE_COMMUNICATOR_UDP_HANDSHAKE); + uhs.purpose.size = htonl (sizeof (uhs)); + uhs.sender = uc->sender; + uhs.receiver = my_identity; + uhs.ephemeral = *ephemeral; + uhs.monotonic_time = uc->monotonic_time; + return GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_COMMUNICATOR_UDP_HANDSHAKE, + &uhs.purpose, + &uc->sender_sig, + &uc->sender.public_key); +} + + /** * Socket read task. * @@ -1397,6 +1629,7 @@ sock_read (void *cls) "recv"); return; } + /* first, see if it is a UDPBox */ if (rcvd > sizeof (struct UDPBox)) { @@ -1404,8 +1637,8 @@ sock_read (void *cls) struct KeyCacheEntry *kce; box = (const struct UDPBox *) buf; - kce = GNUNET_CONTAINER_multihashmap_get (key_cache, - &box->kid); + kce = GNUNET_CONTAINER_multishortmap_get (key_cache, + &box->kid); if (NULL != kce) { decrypt_box (box, @@ -1414,7 +1647,39 @@ sock_read (void *cls) return; } } - /* next, test if it is a KX */ + + /* next, check if it is a broadcast */ + if (sizeof (struct UDPBroadcast) == rcvd) + { + const struct UDPBroadcast *ub; + struct UdpBroadcastSignature uhs; + + ub = (const struct UDPBroadcast *) buf; + uhs.purpose.purpose = htonl (GNUNET_SIGNATURE_COMMUNICATOR_UDP_BROADCAST); + uhs.purpose.size = htonl (sizeof (uhs)); + uhs.sender = ub->sender; + GNUNET_CRYPTO_hash (&sa, + salen, + &uhs.h_address); + if (GNUNET_OK == + GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_COMMUNICATOR_UDP_BROADCAST, + &uhs.purpose, + &ub->sender_sig, + &ub->sender.public_key)) + { + GNUNET_STATISTICS_update (stats, + "# broadcasts received", + 1, + GNUNET_NO); + // FIXME #5551: we effectively just got a HELLO! + // trigger verification NOW! + return; + } + /* continue with KX, mostly for statistics... */ + } + + + /* finally, test if it is a KX */ if (rcvd < sizeof (struct UDPConfirmation) + sizeof (struct InitialKX)) { GNUNET_STATISTICS_update (stats, @@ -1432,13 +1697,13 @@ sock_read (void *cls) struct SenderAddress *sender; kx = (const struct InitialKX *) buf; - ss = setup_shared_secret_dec (&kx->ephemral); + ss = setup_shared_secret_dec (&kx->ephemeral); if (GNUNET_OK != try_decrypt (ss, - 0, kx->gcm_tag, + 0, &buf[sizeof (*kx)], - (const struct GNUNET_CRYPTO_EcdhePublicKey *) buf, + sizeof (pbuf), pbuf)) { GNUNET_free (ss); @@ -1461,7 +1726,7 @@ sock_read (void *cls) GNUNET_NO); return; } - calculcate_cmac (ss); + calculate_cmac (ss); sender = setup_sender (&uc->sender, (const struct sockaddr *) &sa, salen); @@ -1518,10 +1783,12 @@ udp_address_to_sockaddr (const char *bindto, bindto); return NULL; } - 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; @@ -1625,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. @@ -1648,22 +1952,31 @@ mq_send (struct GNUNET_MQ_Handle *mq, receiver_destroy (receiver); return; } + 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 UDPConfirmation uc; struct InitialKX kx; struct GNUNET_CRYPTO_EcdhePrivateKey epriv; char dgram[receiver->mtu + sizeof (uc) + sizeof (kx)]; size_t dpos; + gcry_cipher_hd_t out_cipher; + struct SharedSecret *ss; + /* setup key material */ GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_ecdhe_key_create2 (&epriv)); + GNUNET_CRYPTO_ecdhe_key_create2 (&epriv)); + + ss = setup_shared_secret_enc (&epriv, + receiver); + setup_cipher (&ss->master, + 0, + &out_cipher); /* compute 'uc' */ uc.sender = my_identity; uc.monotonic_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (cfg)); @@ -1696,37 +2009,16 @@ mq_send (struct GNUNET_MQ_Handle *mq, msg, msize)); dpos += msize; - /* Pad to MTU */ - { - char pad[sizeof (dgram) - pos]; - - 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 == gcry_cipher_gettag (out_cipher, kx.gcm_tag, sizeof (kx.gcm_tag))); + gcry_cipher_close (out_cipher); memcpy (dgram, &kx, sizeof (kx)); @@ -1739,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); } @@ -1803,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. @@ -1811,7 +2232,7 @@ mq_error (void *cls, */ static struct ReceiverAddress * receiver_setup (const struct GNUNET_PeerIdentity *target, - const struct sockddr *address, + const struct sockaddr *address, socklen_t address_len) { struct ReceiverAddress *receiver; @@ -1832,55 +2253,37 @@ receiver_setup (const struct GNUNET_PeerIdentity *target, = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT); 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); + receiver->timeout.abs_value_us); 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(queue->address, - queue->address_len)); - break; - case AF_INET6: - GNUNET_asprintf (&foreign_addr, - "%s-%s", - COMMUNICATOR_ADDRESS_PREFIX, - GNUNET_a2s(queue->address, - queue->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; } @@ -1924,6 +2327,7 @@ mq_init (void *cls, receiver = receiver_setup (peer, in, in_len); + (void) receiver; return GNUNET_OK; } @@ -1959,9 +2363,9 @@ get_receiver_delete_it (void *cls, * @return #GNUNET_OK to continue to iterate */ static int -get_receiver_delete_it (void *cls, - const struct GNUNET_PeerIdentity *target, - void *value) +get_sender_delete_it (void *cls, + const struct GNUNET_PeerIdentity *target, + void *value) { struct SenderAddress *sender = value; @@ -1985,6 +2389,13 @@ do_shutdown (void *cls) GNUNET_NAT_unregister (nat); nat = NULL; } + while (NULL != bi_head) + bi_destroy (bi_head); + if (NULL != broadcast_task) + { + GNUNET_SCHEDULER_cancel (broadcast_task); + broadcast_task = NULL; + } if (NULL != read_task) { GNUNET_SCHEDULER_cancel (read_task); @@ -2055,7 +2466,7 @@ enc_notify_cb (void *cls, return; } ack = (const struct UDPAck *) msg; - GNUNET_CONTAINER_multihashmap_get_multiple (receivers, + GNUNET_CONTAINER_multipeermap_get_multiple (receivers, sender, &handle_ack, (void *) ack); @@ -2114,6 +2525,240 @@ nat_address_cb (void *cls, } +/** + * Broadcast our presence on one of our interfaces. + * + * @param cls a `struct BroadcastInterface` + */ +static void +ifc_broadcast (void *cls) +{ + struct BroadcastInterface *bi = cls; + struct GNUNET_TIME_Relative delay; + + delay = BROADCAST_FREQUENCY; + delay.rel_value_us = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + delay.rel_value_us); + bi->broadcast_task + = GNUNET_SCHEDULER_add_delayed (INTERFACE_SCAN_FREQUENCY, + &ifc_broadcast, + bi); + + switch (bi->sa->sa_family) { + case AF_INET: + { + static int yes = 1; + static int no = 0; + ssize_t sent; + + if (GNUNET_OK != + GNUNET_NETWORK_socket_setsockopt (udp_sock, + SOL_SOCKET, + SO_BROADCAST, + &yes, + sizeof (int))) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "setsockopt"); + sent = GNUNET_NETWORK_socket_sendto (udp_sock, + &bi->bcm, + sizeof (bi->bcm), + bi->ba, + bi->salen); + if (-1 == sent) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "sendto"); + if (GNUNET_OK != + GNUNET_NETWORK_socket_setsockopt (udp_sock, + SOL_SOCKET, + SO_BROADCAST, + &no, + sizeof (int))) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "setsockopt"); + break; + } + case AF_INET6: + { + ssize_t sent; + struct sockaddr_in6 dst; + + dst.sin6_family = AF_INET6; + dst.sin6_port = htons (my_port); + dst.sin6_addr = bi->mcreq.ipv6mr_multiaddr; + dst.sin6_scope_id = ((struct sockaddr_in6*) bi->ba)->sin6_scope_id; + + sent = GNUNET_NETWORK_socket_sendto (udp_sock, + &bi->bcm, + sizeof (bi->bcm), + (const struct sockaddr *) + &dst, + sizeof (dst)); + if (-1 == sent) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "sendto"); + break; + } + default: + GNUNET_break (0); + break; + } +} + + +/** + * Callback function invoked for each interface found. + * Activates/deactivates broadcast interfaces. + * + * @param cls NULL + * @param name name of the interface (can be NULL for unknown) + * @param isDefault is this presumably the default interface + * @param addr address of this interface (can be NULL for unknown or unassigned) + * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned) + * @param netmask the network mask (can be NULL for unknown or unassigned) + * @param addrlen length of the address + * @return #GNUNET_OK to continue iteration, #GNUNET_SYSERR to abort + */ +static int +iface_proc (void *cls, + const char *name, + int isDefault, + const struct sockaddr *addr, + const struct sockaddr *broadcast_addr, + const struct sockaddr *netmask, socklen_t addrlen) +{ + struct BroadcastInterface *bi; + enum GNUNET_NetworkType network; + struct UdpBroadcastSignature ubs; + + (void) cls; + (void) netmask; + network = GNUNET_NT_scanner_get_type (is, + addr, + addrlen); + if (GNUNET_NT_LOOPBACK == network) + { + /* Broadcasting on loopback does not make sense */ + return GNUNET_YES; + } + if (NULL == addr) + return GNUNET_YES; /* need to know our address! */ + for (bi = bi_head; NULL != bi; bi = bi->next) + { + if ( (bi->salen == addrlen) && + (0 == memcmp (addr, + bi->sa, + addrlen)) ) + { + bi->found = GNUNET_YES; + return GNUNET_OK; + } + } + + if ( (AF_INET6 == addr->sa_family) && + (NULL == broadcast_addr) ) + return GNUNET_OK; /* broadcast_addr is required for IPv6! */ + if ( (AF_INET6 == addr->sa_family) && + (GNUNET_YES != have_v6_socket) ) + return GNUNET_OK; /* not using IPv6 */ + + bi = GNUNET_new (struct BroadcastInterface); + bi->sa = GNUNET_memdup (addr, + addrlen); + if (NULL != broadcast_addr) + bi->ba = GNUNET_memdup (broadcast_addr, + addrlen); + bi->salen = addrlen; + bi->found = GNUNET_YES; + bi->bcm.sender = my_identity; + ubs.purpose.purpose = htonl (GNUNET_SIGNATURE_COMMUNICATOR_UDP_BROADCAST); + ubs.purpose.size = htonl (sizeof (ubs)); + ubs.sender = my_identity; + GNUNET_CRYPTO_hash (addr, + addrlen, + &ubs.h_address); + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_eddsa_sign (my_private_key, + &ubs.purpose, + &bi->bcm.sender_sig)); + bi->broadcast_task = GNUNET_SCHEDULER_add_now (&ifc_broadcast, + bi); + GNUNET_CONTAINER_DLL_insert (bi_head, + bi_tail, + bi); + if ( (AF_INET6 == addr->sa_family) && + (NULL != broadcast_addr) ) + { + /* Create IPv6 multicast request */ + const struct sockaddr_in6 *s6 + = (const struct sockaddr_in6 *) broadcast_addr; + + GNUNET_assert (1 == + inet_pton (AF_INET6, + "FF05::13B", + &bi->mcreq.ipv6mr_multiaddr)); + + /* http://tools.ietf.org/html/rfc2553#section-5.2: + * + * IPV6_JOIN_GROUP + * + * Join a multicast group on a specified local interface. If the + * interface index is specified as 0, the kernel chooses the local + * interface. For example, some kernels look up the multicast + * group in the normal IPv6 routing table and using the resulting + * interface; we do this for each interface, so no need to use + * zero (anymore...). + */ + bi->mcreq.ipv6mr_interface = s6->sin6_scope_id; + + /* Join the multicast group */ + if (GNUNET_OK != + GNUNET_NETWORK_socket_setsockopt + (udp_sock, + IPPROTO_IPV6, + IPV6_JOIN_GROUP, + &bi->mcreq, + sizeof (bi->mcreq))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "setsockopt"); + } + } + return GNUNET_OK; +} + + +/** + * Scan interfaces to broadcast our presence on the LAN. + * + * @param cls NULL, unused + */ +static void +do_broadcast (void *cls) +{ + struct BroadcastInterface *bin; + + (void) cls; + for (struct BroadcastInterface *bi = bi_head; + NULL != bi; + bi = bi->next) + bi->found = GNUNET_NO; + GNUNET_OS_network_interfaces_list (&iface_proc, + NULL); + for (struct BroadcastInterface *bi = bi_head; + NULL != bi; + bi = bin) + { + bin = bi->next; + if (GNUNET_NO == bi->found) + bi_destroy (bi); + } + broadcast_task + = GNUNET_SCHEDULER_add_delayed (INTERFACE_SCAN_FREQUENCY, + &do_broadcast, + NULL); +} + + /** * Setup communicator and launch network interactions. * @@ -2147,12 +2792,6 @@ run (void *cls, "BINDTO"); return; } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - COMMUNICATOR_CONFIG_SECTION, - "MAX_QUEUE_LENGTH", - &max_queue_length)) - max_queue_length = DEFAULT_MAX_QUEUE_LENGTH; in = udp_address_to_sockaddr (bindto, &in_len); @@ -2175,6 +2814,8 @@ run (void *cls, GNUNET_free (bindto); return; } + if (AF_INET6 == in->sa_family) + have_v6_socket = GNUNET_YES; if (GNUNET_OK != GNUNET_NETWORK_socket_bind (udp_sock, in, @@ -2184,7 +2825,7 @@ run (void *cls, "bind", bindto); GNUNET_NETWORK_socket_close (udp_sock); - listen_sock = NULL; + udp_sock = NULL; GNUNET_free (in); GNUNET_free (bindto); return; @@ -2209,6 +2850,18 @@ run (void *cls, "Bound to `%s'\n", GNUNET_a2s ((const struct sockaddr *) &in_sto, sto_len)); + switch (in->sa_family) + { + case AF_INET: + my_port = ntohs (((struct sockaddr_in *) in)->sin_port); + break; + case AF_INET6: + my_port = ntohs (((struct sockaddr_in6 *) in)->sin6_port); + break; + default: + GNUNET_break (0); + my_port = 0; + } stats = GNUNET_STATISTICS_create ("C-UDP", cfg); senders = GNUNET_CONTAINER_multipeermap_create (32, @@ -2232,7 +2885,7 @@ run (void *cls, } GNUNET_CRYPTO_eddsa_key_get_public (my_private_key, &my_identity.public_key); - /* start listening */ + /* start reading */ read_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, udp_sock, &sock_read, @@ -2251,6 +2904,15 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } + /* start broadcasting */ + if (GNUNET_YES != + GNUNET_CONFIGURATION_get_value_yesno (cfg, + COMMUNICATOR_CONFIG_SECTION, + "DISABLE_BROADCAST")) + { + broadcast_task = GNUNET_SCHEDULER_add_now (&do_broadcast, + NULL); + } nat = GNUNET_NAT_register (cfg, COMMUNICATOR_CONFIG_SECTION, IPPROTO_UDP,