X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftransport%2Fgnunet-service-tng.c;h=75763fd9d3bf66d7808367dd5ab36981675db3bd;hb=11ae9f030e13d07c505a4d5477d592ddb4b3acf6;hp=56ba70b9a337878d89bc2140f50f6f13f601fe63;hpb=196435205b803f01976560037c69553ac0e8e307;p=oweals%2Fgnunet.git diff --git a/src/transport/gnunet-service-tng.c b/src/transport/gnunet-service-tng.c index 56ba70b9a..75763fd9d 100644 --- a/src/transport/gnunet-service-tng.c +++ b/src/transport/gnunet-service-tng.c @@ -1,19 +1,19 @@ /* - This file is part of GNUnet. - Copyright (C) 2010-2016, 2018, 2019 GNUnet e.V. + This file is part of GNUnet. + Copyright (C) 2010-2016, 2018, 2019 GNUnet e.V. - GNUnet is free software: you can redistribute it and/or modify it - under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, - or (at your option) any later version. + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Affero General Public License for more details. + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . SPDX-License-Identifier: AGPL3.0-or-later */ @@ -24,69 +24,53 @@ * * TODO: * Implement next: - * - complete backchannel signature verification and - * forwarding of backchannel messages to communicators! - * - track RTT, distance, loss, etc. => requires extra data structures! - * - proper use/initialization of timestamps in messages exchanged - * during DV learning - * - persistence of monotonic time obtained from other peers - * in PEERSTORE (by message type) - * - change transport-core API to provide proper flow control in both - * directions, allow multiple messages per peer simultaneously (tag - * confirmations with unique message ID), and replace quota-out with - * proper flow control; specify transmission preferences (latency, - * reliability, etc.) per message! - * - add logging - * - * Later: * - review retransmission logic, right now there is no smartness there! - * => congestion control, flow control, etc (requires RTT, loss, etc.) + * => congestion control, etc [PERFORMANCE-BASICS] + * + * Optimizations-Statistics: + * - Track ACK losses based on ACK-counter [ROUTING] + * - Need to track total bandwidth per VirtualLink and adjust how frequently + * we send FC messages based on bandwidth-delay-product (and relation + * to the window size!). See OPTIMIZE-FC-BDP. + * - Consider more statistics in #check_connection_quality() [FIXME-CONQ-STATISTICS] + * - Adapt available_fc_window_size, using larger values for high-bandwidth + * and high-latency links *if* we have the RAM [GOODPUT / utilization / stalls] + * - Set last_window_consum_limit promise properly based on + * latency and bandwidth of the respective connection [GOODPUT / utilization / stalls] + * + * Optimizations-DV: + * - When forwarding DV learn messages, if a peer is reached that + * has a *bidirectional* link to the origin beyond 1st hop, + * do NOT forward it to peers _other_ than the origin, as + * there is clearly a better path directly from the origin to + * whatever else we could reach. + * - When we passively learned DV (with unconfirmed freshness), we + * right now add the path to our list but with a zero path_valid_until + * time and only use it for unconfirmed routes. However, we could consider + * triggering an explicit validation mechansim ourselves, specifically routing + * a challenge-response message over the path [ROUTING] + * = if available, try to confirm unconfirmed DV paths when trying to establish + * virtual link for a `struct IncomingRequest`. (i.e. if DVH is + * unconfirmed, incoming requests cause us to try to validate a passively + * learned path (requires new message type!)) + * + * Optimizations-Fragmentation: + * - Fragments send over a reliable channel could do without the + * AcknowledgementUUIDP altogether, as they won't be acked! [BANDWIDTH] + * (-> have 2nd type of acknowledgment message; low priority, as we + * do not have an MTU-limited *reliable* communicator) [FIXME-FRAG-REL-UUID] + * - if messages are below MTU, consider adding ACKs and other stuff + * to the same transmission to avoid tiny messages (requires planning at + * receiver, and additional MST-style demultiplex at receiver!) [PACKET COUNT] * - * Optimizations: - * - use shorthashmap on msg_uuid's when matching reliability/fragment ACKs - * against our pending message queue (requires additional per neighbour - * hash map to be maintained, avoids possible linear scan on pending msgs) - * - queue_send_msg and route_message both by API design have to make copies + * Optimizations-internals: + * - queue_send_msg by API design has to make a copy * of the payload, and route_message on top of that requires a malloc/free. - * Change design to approximate "zero" copy better... + * Change design to approximate "zero" copy better... [CPU] * - could avoid copying body of message into each fragment and keep * fragments as just pointers into the original message and only * fully build fragments just before transmission (optimization, should - * reduce CPU and memory use) - * - if messages are below MTU, consider adding ACKs and other stuff - * (requires planning at receiver, and additional MST-style demultiplex - * at receiver!) - * - When we passively learned DV (with unconfirmed freshness), we - * right now add the path to our list but with a zero path_valid_until - * time and only use it for unconfirmed routes. However, we could consider - * triggering an explicit validation mechansim ourselves, specifically routing - * a challenge-response message over the path (OPTIMIZATION-FIXME). - * - * Design realizations / discussion: - * - communicators do flow control by calling MQ "notify sent" - * when 'ready'. They determine flow implicitly (i.e. TCP blocking) - * or explicitly via backchannel FC ACKs. As long as the - * channel is not full, they may 'notify sent' even if the other - * peer has not yet confirmed receipt. The other peer confirming - * is _only_ for FC, not for more reliable transmission; reliable - * transmission (i.e. of fragments) is left to _transport_. - * - ACKs sent back in uni-directional communicators are done via - * the background channel API; here transport _may_ initially - * broadcast (with bounded # hops) if no path is known; - * - transport should _integrate_ DV-routing and build a view of - * the network; then background channel traffic can be - * routed via DV as well as explicit "DV" traffic. - * - background channel is also used for ACKs and NAT traversal support - * - transport service is responsible for AEAD'ing the background - * channel, timestamps and monotonic time are used against replay - * of old messages -> peerstore needs to be supplied with - * "latest timestamps seen" data - * - if transport implements DV, we likely need a 3rd peermap - * in addition to ephemerals and (direct) neighbours - * ==> check if stuff needs to be moved out of "Neighbour" - * - transport should encapsualte core-level messages and do its - * own ACKing for RTT/goodput/loss measurements _and_ fragment - * for retransmission + * reduce CPU and memory use) [CPU, MEMORY] */ #include "platform.h" #include "gnunet_util_lib.h" @@ -97,6 +81,25 @@ #include "gnunet_signatures.h" #include "transport.h" +/** + * Maximum number of messages we acknowledge together in one + * cummulative ACK. Larger values may save a bit of bandwidth. + */ +#define MAX_CUMMULATIVE_ACKS 64 + +/** + * What is the 1:n chance that we send a Flow control response when + * receiving a flow control message that did not change anything for + * us? Basically, this is used in the case where both peers are stuck + * on flow control (no window changes), but one might continue sending + * flow control messages to the other peer as the first FC message + * when things stalled got lost, and then subsequently the other peer + * does *usually* not respond as nothing changed. So to ensure that + * eventually the FC messages stop, we do send with 1/8th probability + * an FC message even if nothing changed. That prevents one peer + * being stuck in sending (useless) FC messages "forever". + */ +#define FC_NO_CHANGE_REPLY_PROBABILITY 8 /** * What is the size we assume for a read operation in the @@ -104,6 +107,44 @@ */ #define IN_PACKET_SIZE_WITHOUT_MTU 128 +/** + * Number of slots we keep of historic data for computation of + * goodput / message loss ratio. + */ +#define GOODPUT_AGING_SLOTS 4 + +/** + * How big is the flow control window size by default; + * limits per-neighbour RAM utilization. + */ +#define DEFAULT_WINDOW_SIZE (128 * 1024) + +/** + * For how many incoming connections do we try to create a + * virtual link for (at the same time!). This does NOT + * limit the number of incoming connections, just the number + * for which we are actively trying to find working addresses + * in the absence (!) of our own applications wanting the + * link to go up. + */ +#define MAX_INCOMING_REQUEST 16 + +/** + * Maximum number of peers we select for forwarding DVInit + * messages at the same time (excluding initiator). + */ +#define MAX_DV_DISCOVERY_SELECTION 16 + +/** + * Window size. How many messages to the same target do we pass + * to CORE without a RECV_OK in between? Small values limit + * thoughput, large values will increase latency. + * + * FIXME-OPTIMIZE: find out what good values are experimentally, + * maybe set adaptively (i.e. to observed available bandwidth). + */ +#define RECV_WINDOW_SIZE 4 + /** * Minimum number of hops we should forward DV learn messages * even if they are NOT useful for us in hope of looping @@ -137,6 +178,13 @@ #define DELAY_WARN_THRESHOLD \ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) +/** + * If a DVBox could not be forwarded after this number of + * seconds we drop it. + */ +#define DV_FORWARD_TIMEOUT \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60) + /** * We only consider queues as "quality" connections when * suppressing the generation of DV initiation messages if @@ -152,6 +200,13 @@ #define DV_PATH_VALIDITY_TIMEOUT \ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) +/** + * How long do we cache backchannel (struct Backtalker) information + * after a backchannel goes inactive? + */ +#define BACKCHANNEL_INACTIVITY_TIMEOUT \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5) + /** * How long before paths expire would we like to (re)discover DV paths? Should * be below #DV_PATH_VALIDITY_TIMEOUT. @@ -184,6 +239,14 @@ #define MAX_VALIDATION_CHALLENGE_FREQ \ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_DAYS, 1) +/** + * How long until we forget about historic accumulators and thus + * reset the ACK counter? Should exceed the maximum time an + * active connection experiences without an ACK. + */ +#define ACK_CUMMULATOR_TIMEOUT \ + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4) + /** * What is the non-randomized base frequency at which we * would initiate DV learn messages? @@ -243,55 +306,64 @@ GNUNET_NETWORK_STRUCT_BEGIN /** - * Outer layer of an encapsulated backchannel message. + * Unique identifier we attach to a message. */ -struct TransportBackchannelEncapsulationMessage +struct MessageUUIDP { /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_BACKCHANNEL_ENCAPSULATION. + * Unique value, generated by incrementing the + * `message_uuid_ctr` of `struct Neighbour`. */ - struct GNUNET_MessageHeader header; + uint64_t uuid GNUNET_PACKED; +}; - /** - * Reserved, always zero. - */ - uint32_t reserved GNUNET_PACKED; +/** + * Unique identifier to map an acknowledgement to a transmission. + */ +struct AcknowledgementUUIDP +{ /** - * Target's peer identity (as backchannels may be transmitted - * indirectly, or even be broadcast). + * The UUID value. */ - struct GNUNET_PeerIdentity target; + struct GNUNET_Uuid value; +}; - /** - * Ephemeral key setup by the sender for @e target, used - * to encrypt the payload. - */ - struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key; +/** + * Type of a nonce used for challenges. + */ +struct ChallengeNonceP +{ /** - * We use an IV here as the @e ephemeral_key is re-used for - * #EPHEMERAL_VALIDITY time to avoid re-signing it all the time. + * The value of the nonce. Note that this is NOT a hash. */ - struct GNUNET_ShortHashCode iv; + struct GNUNET_ShortHashCode value; +}; + +/** + * Outer layer of an encapsulated backchannel message. + */ +struct TransportBackchannelEncapsulationMessage +{ /** - * HMAC over the ciphertext of the encrypted, variable-size - * body that follows. Verified via DH of @e target and - * @e ephemeral_key + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_BACKCHANNEL_ENCAPSULATION. */ - struct GNUNET_HashCode hmac; + struct GNUNET_MessageHeader header; - /* Followed by encrypted, variable-size payload */ + /* Followed by *another* message header which is the message to + the communicator */ + + /* Followed by a 0-terminated name of the communicator */ }; /** * Body by which a peer confirms that it is using an ephemeral key. */ -struct EphemeralConfirmation +struct EphemeralConfirmationPS { - /** * Purpose is #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL */ @@ -311,7 +383,7 @@ struct EphemeralConfirmation * communicators must protect against replay attacks when using backchannel * communication! */ - struct GNUNET_TIME_AbsoluteNBO ephemeral_validity; + struct GNUNET_TIME_AbsoluteNBO sender_monotonic_time; /** * Target's peer identity. @@ -330,9 +402,8 @@ struct EphemeralConfirmation * Plaintext of the variable-size payload that is encrypted * within a `struct TransportBackchannelEncapsulationMessage` */ -struct TransportBackchannelRequestPayload +struct TransportDVBoxPayloadP { - /** * Sender's peer identity. */ @@ -344,22 +415,6 @@ struct TransportBackchannelRequestPayload */ struct GNUNET_CRYPTO_EddsaSignature sender_sig; - /** - * How long is this signature over the ephemeral key valid? - * - * Note that the receiver MUST IGNORE the absolute time, and only interpret - * the value as a mononic time and reject "older" values than the last one - * observed. This is necessary as we do not want to require synchronized - * clocks and may not have a bidirectional communication channel. - * - * Even with this, there is no real guarantee against replay achieved here, - * unless the latest timestamp is persisted. While persistence should be - * provided via PEERSTORE, we do not consider the mechanism reliable! Thus, - * communicators must protect against replay attacks when using backchannel - * communication! - */ - struct GNUNET_TIME_AbsoluteNBO ephemeral_validity; - /** * Current monotonic time of the sending transport service. Used to * detect replayed messages. Note that the receiver should remember @@ -373,10 +428,7 @@ struct TransportBackchannelRequestPayload struct GNUNET_TIME_AbsoluteNBO monotonic_time; /* Followed by a `struct GNUNET_MessageHeader` with a message - for a communicator */ - - /* Followed by a 0-termianted string specifying the name of - the communicator which is to receive the message */ + for the target peer */ }; @@ -384,7 +436,7 @@ struct TransportBackchannelRequestPayload * Outer layer of an encapsulated unfragmented application message sent * over an unreliable channel. */ -struct TransportReliabilityBox +struct TransportReliabilityBoxMessage { /** * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_BOX @@ -404,7 +456,27 @@ struct TransportReliabilityBox * messages sent over possibly unreliable channels. Should * be a random. */ - struct GNUNET_ShortHashCode msg_uuid; + struct AcknowledgementUUIDP ack_uuid; +}; + + +/** + * Acknowledgement payload. + */ +struct TransportCummulativeAckPayloadP +{ + /** + * How long was the ACK delayed for generating cummulative ACKs? + * Used to calculate the correct network RTT by taking the receipt + * time of the ack minus the transmission time of the sender minus + * this value. + */ + struct GNUNET_TIME_RelativeNBO ack_delay; + + /** + * UUID of a message being acknowledged. + */ + struct AcknowledgementUUIDP ack_uuid; }; @@ -423,19 +495,12 @@ struct TransportReliabilityAckMessage struct GNUNET_MessageHeader header; /** - * Reserved. Zero. - */ - uint32_t reserved GNUNET_PACKED; - - /** - * How long was the ACK delayed relative to the average time of - * receipt of the messages being acknowledged? Used to calculate - * the average RTT by taking the receipt time of the ack minus the - * average transmission time of the sender minus this value. + * Counter of ACKs transmitted by the sender to us. Incremented + * by one for each ACK, used to detect how many ACKs were lost. */ - struct GNUNET_TIME_RelativeNBO avg_ack_delay; + uint32_t ack_counter GNUNET_PACKED; - /* followed by any number of `struct GNUNET_ShortHashCode` + /* followed by any number of `struct TransportCummulativeAckPayloadP` messages providing ACKs */ }; @@ -443,29 +508,13 @@ struct TransportReliabilityAckMessage /** * Outer layer of an encapsulated fragmented application message. */ -struct TransportFragmentBox +struct TransportFragmentBoxMessage { /** * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT */ struct GNUNET_MessageHeader header; - /** - * Unique ID of this fragment (and fragment transmission!). Will - * change even if a fragement is retransmitted to make each - * transmission attempt unique! Should be incremented by one for - * each fragment transmission. If a client receives a duplicate - * fragment (same @e frag_off), it must send - * #GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT_ACK immediately. - */ - uint32_t frag_uuid GNUNET_PACKED; - - /** - * Original message ID for of the message that all the1 - * fragments belong to. Must be the same for all fragments. - */ - struct GNUNET_ShortHashCode msg_uuid; - /** * Offset of this fragment in the overall message. */ @@ -475,54 +524,21 @@ struct TransportFragmentBox * Total size of the message that is being fragmented. */ uint16_t msg_size GNUNET_PACKED; -}; - - -/** - * Outer layer of an fragmented application message sent over a queue - * with finite MTU. When a #GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT is - * received, the receiver has two RTTs or 64 further fragments with - * the same basic message time to send an acknowledgement, possibly - * acknowledging up to 65 fragments in one ACK. ACKs must also be - * sent immediately once all fragments were sent. - */ -struct TransportFragmentAckMessage -{ - /** - * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT_ACK - */ - struct GNUNET_MessageHeader header; - - /** - * Unique ID of the lowest fragment UUID being acknowledged. - */ - uint32_t frag_uuid GNUNET_PACKED; - - /** - * Bitfield of up to 64 additional fragments following the - * @e msg_uuid being acknowledged by this message. - */ - uint64_t extra_acks GNUNET_PACKED; - - /** - * Original message ID for of the message that all the - * fragments belong to. - */ - struct GNUNET_ShortHashCode msg_uuid; /** - * How long was the ACK delayed relative to the average time of - * receipt of the fragments being acknowledged? Used to calculate - * the average RTT by taking the receipt time of the ack minus the - * average transmission time of the sender minus this value. + * Unique ID of this fragment (and fragment transmission!). Will + * change even if a fragement is retransmitted to make each + * transmission attempt unique! If a client receives a duplicate + * fragment (same @e frag_off for same @a msg_uuid, it must send + * #GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_ACK immediately. */ - struct GNUNET_TIME_RelativeNBO avg_ack_delay; + struct AcknowledgementUUIDP ack_uuid; /** - * How long until the receiver will stop trying reassembly - * of this message? + * Original message ID for of the message that all the fragments + * belong to. Must be the same for all fragments. */ - struct GNUNET_TIME_RelativeNBO reassembly_timeout; + struct MessageUUIDP msg_uuid; }; @@ -567,7 +583,7 @@ struct DvInitPS /** * Challenge value used by the initiator to re-identify the path. */ - struct GNUNET_ShortHashCode challenge; + struct ChallengeNonceP challenge; }; @@ -607,13 +623,13 @@ struct DvHopPS /** * Challenge value used by the initiator to re-identify the path. */ - struct GNUNET_ShortHashCode challenge; + struct ChallengeNonceP challenge; }; /** * An entry describing a peer on a path in a - * `struct TransportDVLearn` message. + * `struct TransportDVLearnMessage` message. */ struct DVPathEntryP { @@ -643,7 +659,7 @@ struct DVPathEntryP * zero, peers that can forward to the initator should always try to * forward to the initiator. */ -struct TransportDVLearn +struct TransportDVLearnMessage { /** * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN @@ -673,6 +689,20 @@ struct TransportDVLearn */ struct GNUNET_TIME_RelativeNBO non_network_delay; + /** + * Time at the initiator when generating the signature. + * + * Note that the receiver MUST IGNORE the absolute time, and only interpret + * the value as a mononic time and reject "older" values than the last one + * observed. This is necessary as we do not want to require synchronized + * clocks and may not have a bidirectional communication channel. + * + * Even with this, there is no real guarantee against replay achieved here, + * unless the latest timestamp is persisted. Persistence should be + * provided via PEERSTORE if possible. + */ + struct GNUNET_TIME_AbsoluteNBO monotonic_time; + /** * Signature of this hop over the path, of purpose * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR @@ -687,7 +717,7 @@ struct TransportDVLearn /** * Challenge value used by the initiator to re-identify the path. */ - struct GNUNET_ShortHashCode challenge; + struct ChallengeNonceP challenge; /* Followed by @e num_hops `struct DVPathEntryP` values, excluding the initiator of the DV trace; the last entry is the @@ -709,8 +739,15 @@ struct TransportDVLearn * shortcut. * * If a peer finds itself still on the list, it must drop the message. + * + * The payload of the box can only be decrypted and verified by the + * ultimate receiver. Intermediaries do not learn the sender's + * identity and the path the message has taken. However, the first + * hop does learn the sender as @e total_hops would be zero and thus + * the predecessor must be the origin (so this is not really useful + * for anonymization). */ -struct TransportDVBox +struct TransportDVBoxMessage { /** * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX @@ -720,27 +757,48 @@ struct TransportDVBox /** * Number of total hops this messages travelled. In NBO. * @e origin sets this to zero, to be incremented at - * each hop. + * each hop. Peers should limit the @e total_hops value + * they accept from other peers. */ uint16_t total_hops GNUNET_PACKED; /** - * Number of hops this messages includes. In NBO. + * Number of hops this messages includes. In NBO. Reduced by one + * or more at each hop. Peers should limit the @e num_hops value + * they accept from other peers. */ uint16_t num_hops GNUNET_PACKED; /** - * Identity of the peer that originated the message. + * Ephemeral key setup by the sender for target, used to encrypt the + * payload. Intermediaries must not change this value. + */ + struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key; + + /** + * We use an IV here as the @e ephemeral_key is re-used for + * #EPHEMERAL_VALIDITY time to avoid re-signing it all the time. + * Intermediaries must not change this value. + */ + struct GNUNET_ShortHashCode iv; + + /** + * HMAC over the ciphertext of the encrypted, variable-size body + * that follows. Verified via DH of target and @e ephemeral_key. + * Intermediaries must not change this value. */ - struct GNUNET_PeerIdentity origin; + struct GNUNET_HashCode hmac; /* Followed by @e num_hops `struct GNUNET_PeerIdentity` values; excluding the @e origin and the current peer, the last must be the ultimate target; if @e num_hops is zero, the receiver of this message is the ultimate target. */ - /* Followed by the actual message, which itself may be - another box, but not a DV_LEARN or DV_BOX message! */ + /* Followed by encrypted, variable-size payload, which + must begin with a `struct TransportDVBoxPayloadP` */ + + /* Followed by the actual message, which itself must not be a + a DV_LEARN or DV_BOX message! */ }; @@ -748,27 +806,26 @@ struct TransportDVBox * Message send to another peer to validate that it can indeed * receive messages at a particular address. */ -struct TransportValidationChallenge +struct TransportValidationChallengeMessage { - /** * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_CHALLENGE */ struct GNUNET_MessageHeader header; /** - * Zero. + * Always zero. */ uint32_t reserved GNUNET_PACKED; /** * Challenge to be signed by the receiving peer. */ - struct GNUNET_ShortHashCode challenge; + struct ChallengeNonceP challenge; /** - * Timestamp of the sender, to be copied into the reply - * to allow sender to calculate RTT. + * Timestamp of the sender, to be copied into the reply to allow + * sender to calculate RTT. Must be monotonically increasing! */ struct GNUNET_TIME_AbsoluteNBO sender_time; }; @@ -780,7 +837,6 @@ struct TransportValidationChallenge */ struct TransportValidationPS { - /** * Purpose is #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE */ @@ -795,24 +851,23 @@ struct TransportValidationPS /** * Challenge signed by the receiving peer. */ - struct GNUNET_ShortHashCode challenge; + struct ChallengeNonceP challenge; }; /** - * Message send to a peer to respond to a + * Message send to a peer to respond to a * #GNUNET_MESSAGE_TYPE_ADDRESS_VALIDATION_CHALLENGE */ -struct TransportValidationResponse +struct TransportValidationResponseMessage { - /** * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_RESPONSE */ struct GNUNET_MessageHeader header; /** - * Zero. + * Always zero. */ uint32_t reserved GNUNET_PACKED; @@ -825,7 +880,7 @@ struct TransportValidationResponse /** * The challenge that was signed by the receiving peer. */ - struct GNUNET_ShortHashCode challenge; + struct ChallengeNonceP challenge; /** * Original timestamp of the sender (was @code{sender_time}), @@ -841,6 +896,65 @@ struct TransportValidationResponse }; +/** + * Message for Transport-to-Transport Flow control. Specifies the size + * of the flow control window, including how much we believe to have + * consumed (at transmission time), how much we believe to be allowed + * (at transmission time), and how much the other peer is allowed to + * send to us, and how much data we already received from the other + * peer. + */ +struct TransportFlowControlMessage +{ + /** + * Type is #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL + */ + struct GNUNET_MessageHeader header; + + /** + * Sequence number of the flow control message. Incremented by one + * for each message. Starts at zero when a virtual link goes up. + * Used to detect one-sided connection drops. On wrap-around, the + * flow control counters will be reset as if the connection had + * dropped. + */ + uint32_t seq GNUNET_PACKED; + + /** + * Flow control window size in bytes, in NBO. + * The receiver can send this many bytes at most. + */ + uint64_t inbound_window_size GNUNET_PACKED; + + /** + * How many bytes has the sender sent that count for flow control at + * this time. Used to allow the receiver to estimate the packet + * loss rate. + */ + uint64_t outbound_sent GNUNET_PACKED; + + /** + * Latest flow control window size we learned from the other peer, + * in bytes, in NBO. We are limited to sending at most this many + * bytes to the other peer. May help the other peer detect when + * flow control messages were lost and should thus be retransmitted. + * In particular, if the delta to @e outbound_sent is too small, + * this signals that we are stalled. + */ + uint64_t outbound_window_size GNUNET_PACKED; + + /** + * Timestamp of the sender. Must be monotonically increasing! + * Used to enable receiver to ignore out-of-order packets in + * combination with the @e seq. Note that @e seq will go down + * (back to zero) whenever either side believes the connection + * was dropped, allowing the peers to detect that they need to + * reset the counters for the number of bytes sent! + */ + struct GNUNET_TIME_AbsoluteNBO sender_time; +}; + + GNUNET_NETWORK_STRUCT_END @@ -876,12 +990,46 @@ enum ClientType }; +/** + * Which transmission options are allowable for transmission? + * Interpreted bit-wise! + */ +enum RouteMessageOptions +{ + /** + * Only confirmed, non-DV direct neighbours. + */ + RMO_NONE = 0, + + /** + * We are allowed to use DV routing for this @a hdr + */ + RMO_DV_ALLOWED = 1, + + /** + * We are allowed to use unconfirmed queues or DV routes for this message + */ + RMO_UNCONFIRMED_ALLOWED = 2, + + /** + * Reliable and unreliable, DV and non-DV are all acceptable. + */ + RMO_ANYTHING_GOES = (RMO_DV_ALLOWED | RMO_UNCONFIRMED_ALLOWED), + + /** + * If we have multiple choices, it is OK to send this message + * over multiple channels at the same time to improve loss tolerance. + * (We do at most 2 transmissions.) + */ + RMO_REDUNDANT = 4 +}; + + /** * When did we launch this DV learning activity? */ struct LearnLaunchEntry { - /** * Kept (also) in a DLL sorted by launch time. */ @@ -895,7 +1043,7 @@ struct LearnLaunchEntry /** * Challenge that uniquely identifies this activity. */ - struct GNUNET_ShortHashCode challenge; + struct ChallengeNonceP challenge; /** * When did we transmit the DV learn message (used to calculate RTT) and @@ -906,47 +1054,45 @@ struct LearnLaunchEntry /** - * Entry in our cache of ephemeral keys we currently use. This way, we only - * sign an ephemeral once per @e target, and then can re-use it over multiple - * #GNUNET_MESSAGE_TYPE_TRANSPORT_BACKCHANNEL_ENCAPSULATION messages (as - * signing is expensive and in some cases we may use backchannel messages a - * lot). + * Information we keep per #GOODPUT_AGING_SLOTS about historic + * (or current) transmission performance. */ -struct EphemeralCacheEntry +struct TransmissionHistoryEntry { - /** - * Target's peer identity (we don't re-use ephemerals - * to limit linkability of messages). + * Number of bytes actually sent in the interval. */ - struct GNUNET_PeerIdentity target; + uint64_t bytes_sent; /** - * Signature affirming @e ephemeral_key of type - * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL + * Number of bytes received and acknowledged by the other peer in + * the interval. */ - struct GNUNET_CRYPTO_EddsaSignature sender_sig; + uint64_t bytes_received; +}; - /** - * How long is @e sender_sig valid - */ - struct GNUNET_TIME_Absolute ephemeral_validity; +/** + * Performance data for a transmission possibility. + */ +struct PerformanceData +{ /** - * Our ephemeral key. + * Weighted average for the RTT. */ - struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key; + struct GNUNET_TIME_Relative aged_rtt; /** - * Our private ephemeral key. + * Historic performance data, using a ring buffer of#GOODPUT_AGING_SLOTS + * entries. */ - struct GNUNET_CRYPTO_EcdhePrivateKey private_key; + struct TransmissionHistoryEntry the[GOODPUT_AGING_SLOTS]; /** - * Node in the ephemeral cache for this entry. - * Used for expiration. + * What was the last age when we wrote to @e the? Used to clear + * old entries when the age advances. */ - struct GNUNET_CONTAINER_HeapNode *hn; + unsigned int last_age; }; @@ -955,13 +1101,11 @@ struct EphemeralCacheEntry */ struct TransportClient; - /** * A neighbour that at least one communicator is connected to. */ struct Neighbour; - /** * Entry in our #dv_routes table, representing a (set of) distance * vector routes to a particular peer. @@ -969,799 +1113,1254 @@ struct Neighbour; struct DistanceVector; /** - * One possible hop towards a DV target. + * A queue is a message queue provided by a communicator + * via which we can reach a particular neighbour. */ -struct DistanceVectorHop -{ +struct Queue; - /** - * Kept in a MDLL, sorted by @e timeout. - */ - struct DistanceVectorHop *next_dv; - - /** - * Kept in a MDLL, sorted by @e timeout. - */ - struct DistanceVectorHop *prev_dv; +/** + * Message awaiting transmission. See detailed comments below. + */ +struct PendingMessage; - /** - * Kept in a MDLL. - */ - struct DistanceVectorHop *next_neighbour; +/** + * One possible hop towards a DV target. + */ +struct DistanceVectorHop; - /** - * Kept in a MDLL. - */ - struct DistanceVectorHop *prev_neighbour; +/** + * A virtual link is another reachable peer that is known to CORE. It + * can be either a `struct Neighbour` with at least one confirmed + * `struct Queue`, or a `struct DistanceVector` with at least one + * confirmed `struct DistanceVectorHop`. With a virtual link we track + * data that is per neighbour that is not specific to how the + * connectivity is established. + */ +struct VirtualLink; - /** - * What would be the next hop to @e target? - */ - struct Neighbour *next_hop; +/** + * Context from #handle_incoming_msg(). Closure for many + * message handlers below. + */ +struct CommunicatorMessageContext +{ /** - * Distance vector entry this hop belongs with. + * Kept in a DLL of `struct VirtualLink` if waiting for CORE + * flow control to unchoke. */ - struct DistanceVector *dv; + struct CommunicatorMessageContext *next; /** - * Array of @e distance hops to the target, excluding @e next_hop. - * NULL if the entire path is us to @e next_hop to `target`. Allocated - * at the end of this struct. Excludes the target itself! + * Kept in a DLL of `struct VirtualLink` if waiting for CORE + * flow control to unchoke. */ - const struct GNUNET_PeerIdentity *path; + struct CommunicatorMessageContext *prev; /** - * At what time do we forget about this path unless we see it again - * while learning? + * Which communicator provided us with the message. */ - struct GNUNET_TIME_Absolute timeout; + struct TransportClient *tc; /** - * For how long is the validation of this path considered - * valid? - * Set to ZERO if the path is learned by snooping on DV learn messages - * initiated by other peers, and to the time at which we generated the - * challenge for DV learn operations this peer initiated. + * Additional information for flow control and about the sender. */ - struct GNUNET_TIME_Absolute path_valid_until; + struct GNUNET_TRANSPORT_IncomingMessage im; /** - * Number of hops in total to the `target` (excluding @e next_hop and `target` - * itself). Thus 0 still means a distance of 2 hops (to @e next_hop and then - * to `target`). + * Number of hops the message has travelled (if DV-routed). + * FIXME: make use of this in ACK handling! */ - unsigned int distance; + uint16_t total_hops; }; /** - * Entry in our #dv_routes table, representing a (set of) distance - * vector routes to a particular peer. + * Closure for #core_env_sent_cb. */ -struct DistanceVector +struct CoreSentContext { - - /** - * To which peer is this a route? - */ - struct GNUNET_PeerIdentity target; - - /** - * Known paths to @e target. - */ - struct DistanceVectorHop *dv_head; - /** - * Known paths to @e target. + * Kept in a DLL to clear @e vl in case @e vl is lost. */ - struct DistanceVectorHop *dv_tail; + struct CoreSentContext *next; /** - * Task scheduled to purge expired paths from @e dv_head MDLL. + * Kept in a DLL to clear @e vl in case @e vl is lost. */ - struct GNUNET_SCHEDULER_Task *timeout_task; + struct CoreSentContext *prev; /** - * Task scheduled to possibly notfiy core that this queue is no longer - * counting as confirmed. Runs the #core_queue_visibility_check(). + * Virtual link this is about. */ - struct GNUNET_SCHEDULER_Task *visibility_task; + struct VirtualLink *vl; /** - * Quota at which CORE is allowed to transmit to this peer - * (note that the value CORE should actually be told is this - * value plus the respective value in `struct Neighbour`). - * Should match the sum of the quotas of all of the paths. - * - * FIXME: not yet set, tricky to get right given multiple paths, - * many of which may be inactive! (=> Idea: measure???) - * FIXME: how do we set this value initially when we tell CORE? - * Options: start at a minimum value or at literally zero? - * (=> Current thought: clean would be zero!) + * How big was the message. */ - struct GNUNET_BANDWIDTH_Value32NBO quota_out; + uint16_t size; /** - * Is one of the DV paths in this struct 'confirmed' and thus - * the cause for CORE to see this peer as connected? (Note that - * the same may apply to a `struct Neighbour` at the same time.) + * By how much should we increment @e vl's + * incoming_fc_window_size_used once we are done sending to CORE? + * Use to ensure we do not increment twice if there is more than one + * CORE client. */ - int core_visible; + uint16_t isize; }; /** - * A queue is a message queue provided by a communicator - * via which we can reach a particular neighbour. - */ -struct Queue; - -/** - * Message awaiting transmission. See detailed comments below. - */ -struct PendingMessage; - -/** - * Entry identifying transmission in one of our `struct - * Queue` which still awaits an ACK. This is used to - * ensure we do not overwhelm a communicator and limit the number of - * messages outstanding per communicator (say in case communicator is - * CPU bound) and per queue (in case bandwidth allocation exceeds - * what the communicator can actually provide towards a particular - * peer/target). + * A virtual link is another reachable peer that is known to CORE. It + * can be either a `struct Neighbour` with at least one confirmed + * `struct Queue`, or a `struct DistanceVector` with at least one + * confirmed `struct DistanceVectorHop`. With a virtual link we track + * data that is per neighbour that is not specific to how the + * connectivity is established. */ -struct QueueEntry +struct VirtualLink { - - /** - * Kept as a DLL. - */ - struct QueueEntry *next; - /** - * Kept as a DLL. + * Identity of the peer at the other end of the link. */ - struct QueueEntry *prev; + struct GNUNET_PeerIdentity target; /** - * Queue this entry is queued with. + * Communicators blocked for receiving on @e target as we are waiting + * on the @e core_recv_window to increase. */ - struct Queue *queue; + struct CommunicatorMessageContext *cmc_head; /** - * Pending message this entry is for, or NULL for none. + * Communicators blocked for receiving on @e target as we are waiting + * on the @e core_recv_window to increase. */ - struct PendingMessage *pm; + struct CommunicatorMessageContext *cmc_tail; /** - * Message ID used for this message with the queue used for transmission. + * Head of list of messages pending for this VL. */ - uint64_t mid; -}; - + struct PendingMessage *pending_msg_head; -/** - * A queue is a message queue provided by a communicator - * via which we can reach a particular neighbour. - */ -struct Queue -{ /** - * Kept in a MDLL. + * Tail of list of messages pending for this VL. */ - struct Queue *next_neighbour; + struct PendingMessage *pending_msg_tail; /** - * Kept in a MDLL. + * Kept in a DLL to clear @e vl in case @e vl is lost. */ - struct Queue *prev_neighbour; + struct CoreSentContext *csc_tail; /** - * Kept in a MDLL. + * Kept in a DLL to clear @e vl in case @e vl is lost. */ - struct Queue *prev_client; + struct CoreSentContext *csc_head; /** - * Kept in a MDLL. + * Task scheduled to possibly notfiy core that this peer is no + * longer counting as confirmed. Runs the #core_visibility_check(), + * which checks that some DV-path or a queue exists that is still + * considered confirmed. */ - struct Queue *next_client; + struct GNUNET_SCHEDULER_Task *visibility_task; /** - * Head of DLL of unacked transmission requests. + * Task scheduled to periodically retransmit FC messages (in + * case one got lost). */ - struct QueueEntry *queue_head; + struct GNUNET_SCHEDULER_Task *fc_retransmit_task; /** - * End of DLL of unacked transmission requests. + * Neighbour used by this virtual link, NULL if @e dv is used. */ - struct QueueEntry *queue_tail; + struct Neighbour *n; /** - * Which neighbour is this queue for? + * Distance vector used by this virtual link, NULL if @e n is used. */ - struct Neighbour *neighbour; + struct DistanceVector *dv; /** - * Which communicator offers this queue? + * Sender timestamp of @e n_challenge, used to generate out-of-order + * challenges (as sender's timestamps must be monotonically + * increasing). FIXME: where do we need this? */ - struct TransportClient *tc; + struct GNUNET_TIME_Absolute n_challenge_time; /** - * Address served by the queue. + * When did we last send a + * #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL message? + * Used to determine whether it is time to re-transmit the message. */ - const char *address; + struct GNUNET_TIME_Absolute last_fc_transmission; /** - * Task scheduled for the time when this queue can (likely) transmit the - * next message. Still needs to check with the @e tracker_out to be sure. + * Sender timestamp of the last + * #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL message we have + * received. Note that we do not persist this monotonic time as we + * do not really have to worry about ancient flow control window + * sizes after restarts. */ - struct GNUNET_SCHEDULER_Task *transmit_task; + struct GNUNET_TIME_Absolute last_fc_timestamp; /** - * Task scheduled to possibly notfiy core that this queue is no longer - * counting as confirmed. Runs the #core_queue_visibility_check(). + * Expected RTT from the last FC transmission. (Zero if the last + * attempt failed, but could theoretically be zero even on success.) */ - struct GNUNET_SCHEDULER_Task *visibility_task; + struct GNUNET_TIME_Relative last_fc_rtt; /** - * Our current RTT estimate for this queue. + * Used to generate unique UUIDs for messages that are being + * fragmented. */ - struct GNUNET_TIME_Relative rtt; + uint64_t message_uuid_ctr; /** - * How long do *we* consider this @e address to be valid? In the past or - * zero if we have not yet validated it. Can be updated based on - * challenge-response validations (via address validation logic), or when we - * receive ACKs that we can definitively map to transmissions via this - * queue. + * Memory allocated for this virtual link. Expresses how much RAM + * we are willing to allocate to this virtual link. OPTIMIZE-ME: + * Can be adapted to dedicate more RAM to links that need it, while + * sticking to some overall RAM limit. For now, set to + * #DEFAULT_WINDOW_SIZE. */ - struct GNUNET_TIME_Absolute validated_until; + uint64_t available_fc_window_size; /** - * Message ID generator for transmissions on this queue. + * Memory actually used to buffer packets on this virtual link. + * Expresses how much RAM we are currently using for virtual link. + * Note that once CORE is done with a packet, we decrement the value + * here. */ - uint64_t mid_gen; + uint64_t incoming_fc_window_size_ram; /** - * Unique identifier of this queue with the communicator. + * Last flow control window size we provided to the other peer, in + * bytes. We are allowing the other peer to send this + * many bytes. */ - uint32_t qid; + uint64_t incoming_fc_window_size; /** - * Maximum transmission unit supported by this queue. + * How much of the window did the other peer successfully use (and + * we already passed it on to CORE)? Must be below @e + * incoming_fc_window_size. We should effectively signal the + * other peer that the window is this much bigger at the next + * opportunity / challenge. */ - uint32_t mtu; + uint64_t incoming_fc_window_size_used; /** - * Messages pending. + * What is our current estimate on the message loss rate for the sender? + * Based on the difference between how much the sender sent according + * to the last #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL message + * (@e outbound_sent field) and how much we actually received at that + * time (@e incoming_fc_window_size_used). This delta is then + * added onto the @e incoming_fc_window_size when determining the + * @e outbound_window_size we send to the other peer. Initially zero. + * May be negative if we (due to out-of-order delivery) actually received + * more than the sender claims to have sent in its last FC message. */ - uint32_t num_msg_pending; + int64_t incoming_fc_window_size_loss; /** - * Bytes pending. + * Our current flow control window size in bytes. We + * are allowed to transmit this many bytes to @a n. */ - uint32_t num_bytes_pending; + uint64_t outbound_fc_window_size; /** - * Length of the DLL starting at @e queue_head. + * How much of our current flow control window size have we + * used (in bytes). Must be below + * @e outbound_fc_window_size. */ - unsigned int queue_length; + uint64_t outbound_fc_window_size_used; /** - * Network type offered by this queue. + * What is the most recent FC window the other peer sent us + * in `outbound_window_size`? This is basically the window + * size value the other peer has definitively received from + * us. If it matches @e incoming_fc_window_size, we should + * not send a FC message to increase the FC window. However, + * we may still send an FC message to notify the other peer + * that we received the other peer's FC message. */ - enum GNUNET_NetworkType nt; + uint64_t last_outbound_window_size_received; /** - * Connection status for this queue. + * Generator for the sequence numbers of + * #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL messages we send. */ - enum GNUNET_TRANSPORT_ConnectionStatus cs; + uint32_t fc_seq_gen; /** - * How much outbound bandwidth do we have available for this queue? + * Last sequence number of a + * #GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL message we have + * received. */ - struct GNUNET_BANDWIDTH_Tracker tracker_out; + uint32_t last_fc_seq; /** - * How much inbound bandwidth do we have available for this queue? + * How many more messages can we send to CORE before we exhaust + * the receive window of CORE for this peer? If this hits zero, + * we must tell communicators to stop providing us more messages + * for this peer. In fact, the window can go negative as we + * have multiple communicators, so per communicator we can go + * down by one into the negative range. Furthermore, we count + * delivery per CORE client, so if we had multiple cores, that + * might also cause a negative window size here (as one message + * would decrement the window by one per CORE client). */ - struct GNUNET_BANDWIDTH_Tracker tracker_in; + int core_recv_window; }; /** - * Information we keep for a message that we are reassembling. + * Data structure kept when we are waiting for an acknowledgement. */ -struct ReassemblyContext +struct PendingAcknowledgement { - /** - * Original message ID for of the message that all the - * fragments belong to. + * If @e pm is non-NULL, this is the DLL in which this acknowledgement + * is kept in relation to its pending message. */ - struct GNUNET_ShortHashCode msg_uuid; + struct PendingAcknowledgement *next_pm; /** - * Which neighbour is this context for? + * If @e pm is non-NULL, this is the DLL in which this acknowledgement + * is kept in relation to its pending message. */ - struct Neighbour *neighbour; + struct PendingAcknowledgement *prev_pm; /** - * Entry in the reassembly heap (sorted by expiration). + * If @e queue is non-NULL, this is the DLL in which this acknowledgement + * is kept in relation to the queue that was used to transmit the + * @a pm. */ - struct GNUNET_CONTAINER_HeapNode *hn; + struct PendingAcknowledgement *next_queue; /** - * Bitfield with @e msg_size bits representing the positions - * where we have received fragments. When we receive a fragment, - * we check the bits in @e bitfield before incrementing @e msg_missing. - * - * Allocated after the reassembled message. + * If @e queue is non-NULL, this is the DLL in which this acknowledgement + * is kept in relation to the queue that was used to transmit the + * @a pm. */ - uint8_t *bitfield; + struct PendingAcknowledgement *prev_queue; /** - * Task for sending ACK. We may send ACKs either because of hitting - * the @e extra_acks limit, or based on time and @e num_acks. This - * task is for the latter case. + * If @e dvh is non-NULL, this is the DLL in which this acknowledgement + * is kept in relation to the DVH that was used to transmit the + * @a pm. */ - struct GNUNET_SCHEDULER_Task *ack_task; + struct PendingAcknowledgement *next_dvh; /** - * At what time will we give up reassembly of this message? + * If @e dvh is non-NULL, this is the DLL in which this acknowledgement + * is kept in relation to the DVH that was used to transmit the + * @a pm. */ - struct GNUNET_TIME_Absolute reassembly_timeout; + struct PendingAcknowledgement *prev_dvh; /** - * Average delay of all acks in @e extra_acks and @e frag_uuid. - * Should be reset to zero when @e num_acks is set to 0. + * Pointers for the DLL of all pending acknowledgements. + * This list is sorted by @e transmission time. If the list gets too + * long, the oldest entries are discarded. */ - struct GNUNET_TIME_Relative avg_ack_delay; + struct PendingAcknowledgement *next_pa; /** - * Time we received the last fragment. @e avg_ack_delay must be - * incremented by now - @e last_frag multiplied by @e num_acks. + * Pointers for the DLL of all pending acknowledgements. + * This list is sorted by @e transmission time. If the list gets too + * long, the oldest entries are discarded. */ - struct GNUNET_TIME_Absolute last_frag; + struct PendingAcknowledgement *prev_pa; /** - * Bitfield of up to 64 additional fragments following @e frag_uuid - * to be acknowledged in the next cummulative ACK. + * Unique identifier for this transmission operation. */ - uint64_t extra_acks; + struct AcknowledgementUUIDP ack_uuid; /** - * Unique ID of the lowest fragment UUID to be acknowledged in the - * next cummulative ACK. Only valid if @e num_acks > 0. + * Message that was transmitted, may be NULL if the message was ACKed + * via another channel. */ - uint32_t frag_uuid; + struct PendingMessage *pm; /** - * Number of ACKs we have accumulated so far. Reset to 0 - * whenever we send a #GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT_ACK. + * Distance vector path chosen for this transmission, NULL if transmission + * was to a direct neighbour OR if the path was forgotten in the meantime. */ - unsigned int num_acks; + struct DistanceVectorHop *dvh; /** - * How big is the message we are reassembling in total? + * Queue used for transmission, NULL if the queue has been destroyed + * (which may happen before we get an acknowledgement). */ - uint16_t msg_size; + struct Queue *queue; /** - * How many bytes of the message are still missing? Defragmentation - * is complete when @e msg_missing == 0. + * Time of the transmission, for RTT calculation. */ - uint16_t msg_missing; + struct GNUNET_TIME_Absolute transmission_time; - /* Followed by @e msg_size bytes of the (partially) defragmented original - * message */ - - /* Followed by @e bitfield data */ + /** + * Number of bytes of the original message (to calculate bandwidth). + */ + uint16_t message_size; }; /** - * A neighbour that at least one communicator is connected to. + * One possible hop towards a DV target. */ -struct Neighbour +struct DistanceVectorHop { - /** - * Which peer is this about? + * Kept in a MDLL, sorted by @e timeout. */ - struct GNUNET_PeerIdentity pid; + struct DistanceVectorHop *next_dv; /** - * Map with `struct ReassemblyContext` structs for fragments under - * reassembly. May be NULL if we currently have no fragments from - * this @e pid (lazy initialization). + * Kept in a MDLL, sorted by @e timeout. */ - struct GNUNET_CONTAINER_MultiShortmap *reassembly_map; + struct DistanceVectorHop *prev_dv; /** - * Heap with `struct ReassemblyContext` structs for fragments under - * reassembly. May be NULL if we currently have no fragments from - * this @e pid (lazy initialization). + * Kept in a MDLL. */ - struct GNUNET_CONTAINER_Heap *reassembly_heap; + struct DistanceVectorHop *next_neighbour; /** - * Task to free old entries from the @e reassembly_heap and @e reassembly_map. + * Kept in a MDLL. */ - struct GNUNET_SCHEDULER_Task *reassembly_timeout_task; + struct DistanceVectorHop *prev_neighbour; /** - * Head of list of messages pending for this neighbour. + * Head of DLL of PAs that used our @a path. */ - struct PendingMessage *pending_msg_head; + struct PendingAcknowledgement *pa_head; /** - * Tail of list of messages pending for this neighbour. + * Tail of DLL of PAs that used our @a path. */ - struct PendingMessage *pending_msg_tail; + struct PendingAcknowledgement *pa_tail; /** - * Head of MDLL of DV hops that have this neighbour as next hop. Must be - * purged if this neighbour goes down. + * What would be the next hop to @e target? */ - struct DistanceVectorHop *dv_head; + struct Neighbour *next_hop; /** - * Tail of MDLL of DV hops that have this neighbour as next hop. Must be - * purged if this neighbour goes down. + * Distance vector entry this hop belongs with. */ - struct DistanceVectorHop *dv_tail; + struct DistanceVector *dv; /** - * Head of DLL of queues to this peer. + * Array of @e distance hops to the target, excluding @e next_hop. + * NULL if the entire path is us to @e next_hop to `target`. Allocated + * at the end of this struct. Excludes the target itself! */ - struct Queue *queue_head; + const struct GNUNET_PeerIdentity *path; /** - * Tail of DLL of queues to this peer. + * At what time do we forget about this path unless we see it again + * while learning? */ - struct Queue *queue_tail; + struct GNUNET_TIME_Absolute timeout; /** - * Task run to cleanup pending messages that have exceeded their timeout. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * Quota at which CORE is allowed to transmit to this peer - * (note that the value CORE should actually be told is this - * value plus the respective value in `struct DistanceVector`). - * Should match the sum of the quotas of all of the queues. - * - * FIXME: not yet set, tricky to get right given multiple queues! - * (=> Idea: measure???) - * FIXME: how do we set this value initially when we tell CORE? - * Options: start at a minimum value or at literally zero? - * (=> Current thought: clean would be zero!) + * For how long is the validation of this path considered + * valid? + * Set to ZERO if the path is learned by snooping on DV learn messages + * initiated by other peers, and to the time at which we generated the + * challenge for DV learn operations this peer initiated. */ - struct GNUNET_BANDWIDTH_Value32NBO quota_out; + struct GNUNET_TIME_Absolute path_valid_until; /** - * What is the earliest timeout of any message in @e pending_msg_tail? + * Performance data for this transmission possibility. */ - struct GNUNET_TIME_Absolute earliest_timeout; + struct PerformanceData pd; /** - * Do we have a confirmed working queue and are thus visible to - * CORE? + * Number of hops in total to the `target` (excluding @e next_hop and `target` + * itself). Thus 0 still means a distance of 2 hops (to @e next_hop and then + * to `target`). */ - int core_visible; + unsigned int distance; }; /** - * A peer that an application (client) would like us to talk to directly. + * Entry in our #dv_routes table, representing a (set of) distance + * vector routes to a particular peer. */ -struct PeerRequest +struct DistanceVector { - /** - * Which peer is this about? + * To which peer is this a route? */ - struct GNUNET_PeerIdentity pid; + struct GNUNET_PeerIdentity target; /** - * Client responsible for the request. + * Known paths to @e target. */ - struct TransportClient *tc; + struct DistanceVectorHop *dv_head; /** - * Handle for watching the peerstore for HELLOs for this peer. + * Known paths to @e target. */ - struct GNUNET_PEERSTORE_WatchContext *wc; + struct DistanceVectorHop *dv_tail; /** - * What kind of performance preference does this @e tc have? + * Task scheduled to purge expired paths from @e dv_head MDLL. */ - enum GNUNET_MQ_PreferenceKind pk; + struct GNUNET_SCHEDULER_Task *timeout_task; /** - * How much bandwidth would this @e tc like to see? + * Do we have a confirmed working queue and are thus visible to + * CORE? If so, this is the virtual link, otherwise NULL. */ - struct GNUNET_BANDWIDTH_Value32NBO bw; -}; - - -/** - * Types of different pending messages. - */ -enum PendingMessageType -{ + struct VirtualLink *vl; /** - * Ordinary message received from the CORE service. + * Signature affirming @e ephemeral_key of type + * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL */ - PMT_CORE = 0, + struct GNUNET_CRYPTO_EddsaSignature sender_sig; /** - * Fragment box. + * How long is @e sender_sig valid */ - PMT_FRAGMENT_BOX = 1, + struct GNUNET_TIME_Absolute ephemeral_validity; /** - * Reliability box. + * What time was @e sender_sig created */ - PMT_RELIABILITY_BOX = 2, + struct GNUNET_TIME_Absolute monotime; /** - * Any type of acknowledgement. + * Our ephemeral key. */ - PMT_ACKNOWLEDGEMENT = 3, + struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key; /** - * Control traffic generated by the TRANSPORT service itself. + * Our private ephemeral key. */ - PMT_CONTROL = 4 - + struct GNUNET_CRYPTO_EcdhePrivateKey private_key; }; /** - * Transmission request that is awaiting delivery. The original - * transmission requests from CORE may be too big for some queues. - * In this case, a *tree* of fragments is created. At each - * level of the tree, fragments are kept in a DLL ordered by which - * fragment should be sent next (at the head). The tree is searched - * top-down, with the original message at the root. - * - * To select a node for transmission, first it is checked if the - * current node's message fits with the MTU. If it does not, we - * either calculate the next fragment (based on @e frag_off) from the - * current node, or, if all fragments have already been created, - * descend to the @e head_frag. Even though the node was already - * fragmented, the fragment may be too big if the fragment was - * generated for a queue with a larger MTU. In this case, the node - * may be fragmented again, thus creating a tree. - * - * When acknowledgements for fragments are received, the tree - * must be pruned, removing those parts that were already - * acknowledged. When fragments are sent over a reliable - * channel, they can be immediately removed. - * - * If a message is ever fragmented, then the original "full" message - * is never again transmitted (even if it fits below the MTU), and - * only (remaining) fragments are sent. + * Entry identifying transmission in one of our `struct + * Queue` which still awaits an ACK. This is used to + * ensure we do not overwhelm a communicator and limit the number of + * messages outstanding per communicator (say in case communicator is + * CPU bound) and per queue (in case bandwidth allocation exceeds + * what the communicator can actually provide towards a particular + * peer/target). */ -struct PendingMessage +struct QueueEntry { /** - * Kept in a MDLL of messages for this @a target. - */ - struct PendingMessage *next_neighbour; - - /** - * Kept in a MDLL of messages for this @a target. + * Kept as a DLL. */ - struct PendingMessage *prev_neighbour; + struct QueueEntry *next; /** - * Kept in a MDLL of messages from this @a client (if @e pmt is #PMT_CORE) + * Kept as a DLL. */ - struct PendingMessage *next_client; + struct QueueEntry *prev; /** - * Kept in a MDLL of messages from this @a client (if @e pmt is #PMT_CORE) + * Queue this entry is queued with. */ - struct PendingMessage *prev_client; + struct Queue *queue; /** - * Kept in a MDLL of messages from this @a cpm (if @e pmt is - * #PMT_FRAGMENT_BOx) + * Pending message this entry is for, or NULL for none. */ - struct PendingMessage *next_frag; + struct PendingMessage *pm; /** - * Kept in a MDLL of messages from this @a cpm (if @e pmt is - * #PMT_FRAGMENT_BOX) + * Message ID used for this message with the queue used for transmission. */ - struct PendingMessage *prev_frag; + uint64_t mid; +}; - /** - * This message, reliability boxed. Only possibly available if @e pmt is - * #PMT_CORE. - */ - struct PendingMessage *bpm; +/** + * A queue is a message queue provided by a communicator + * via which we can reach a particular neighbour. + */ +struct Queue +{ /** - * Target of the request. + * Kept in a MDLL. */ - struct Neighbour *target; + struct Queue *next_neighbour; /** - * Set to non-NULL value if this message is currently being given to a - * communicator and we are awaiting that communicator's acknowledgement. - * Note that we must not retransmit a pending message while we're still - * in the process of giving it to a communicator. If a pending message - * is free'd while this entry is non-NULL, the @e qe reference to us - * should simply be set to NULL. + * Kept in a MDLL. */ - struct QueueEntry *qe; + struct Queue *prev_neighbour; /** - * Client that issued the transmission request, if @e pmt is #PMT_CORE. + * Kept in a MDLL. */ - struct TransportClient *client; + struct Queue *prev_client; /** - * Head of a MDLL of fragments created for this core message. + * Kept in a MDLL. */ - struct PendingMessage *head_frag; + struct Queue *next_client; /** - * Tail of a MDLL of fragments created for this core message. + * Head of DLL of PAs that used this queue. */ - struct PendingMessage *tail_frag; + struct PendingAcknowledgement *pa_head; /** - * Our parent in the fragmentation tree. + * Tail of DLL of PAs that used this queue. */ - struct PendingMessage *frag_parent; + struct PendingAcknowledgement *pa_tail; /** - * At what time should we give up on the transmission (and no longer retry)? + * Head of DLL of unacked transmission requests. */ - struct GNUNET_TIME_Absolute timeout; + struct QueueEntry *queue_head; /** - * What is the earliest time for us to retry transmission of this message? + * End of DLL of unacked transmission requests. */ - struct GNUNET_TIME_Absolute next_attempt; + struct QueueEntry *queue_tail; /** - * UUID to use for this message (used for reassembly of fragments, only - * initialized if @e msg_uuid_set is #GNUNET_YES). + * Which neighbour is this queue for? */ - struct GNUNET_ShortHashCode msg_uuid; + struct Neighbour *neighbour; /** - * Counter incremented per generated fragment. + * Which communicator offers this queue? */ - uint32_t frag_uuidgen; + struct TransportClient *tc; /** - * Type of the pending message. + * Address served by the queue. */ - enum PendingMessageType pmt; + const char *address; /** - * Size of the original message. + * Task scheduled for the time when this queue can (likely) transmit the + * next message. */ - uint16_t bytes_msg; + struct GNUNET_SCHEDULER_Task *transmit_task; /** - * Offset at which we should generate the next fragment. + * How long do *we* consider this @e address to be valid? In the past or + * zero if we have not yet validated it. Can be updated based on + * challenge-response validations (via address validation logic), or when we + * receive ACKs that we can definitively map to transmissions via this + * queue. */ - uint16_t frag_off; + struct GNUNET_TIME_Absolute validated_until; /** - * #GNUNET_YES once @e msg_uuid was initialized + * Performance data for this queue. */ - int16_t msg_uuid_set; - - /* Followed by @e bytes_msg to transmit */ -}; - - -/** - * One of the addresses of this peer. - */ -struct AddressListEntry -{ + struct PerformanceData pd; /** - * Kept in a DLL. + * Message ID generator for transmissions on this queue to the + * communicator. */ - struct AddressListEntry *next; + uint64_t mid_gen; /** - * Kept in a DLL. + * Unique identifier of this queue with the communicator. */ - struct AddressListEntry *prev; + uint32_t qid; /** - * Which communicator provides this address? + * Maximum transmission unit supported by this queue. */ - struct TransportClient *tc; + uint32_t mtu; /** - * The actual address. + * Messages pending. */ - const char *address; + uint32_t num_msg_pending; /** - * Current context for storing this address in the peerstore. + * Bytes pending. */ - struct GNUNET_PEERSTORE_StoreContext *sc; + uint32_t num_bytes_pending; /** - * Task to periodically do @e st operation. + * Length of the DLL starting at @e queue_head. */ - struct GNUNET_SCHEDULER_Task *st; + unsigned int queue_length; /** - * What is a typical lifetime the communicator expects this - * address to have? (Always from now.) + * Network type offered by this queue. */ - struct GNUNET_TIME_Relative expiration; + enum GNUNET_NetworkType nt; /** - * Address identifier used by the communicator. + * Connection status for this queue. */ - uint32_t aid; + enum GNUNET_TRANSPORT_ConnectionStatus cs; /** - * Network type offered by this address. + * Set to #GNUNET_YES if this queue is idle waiting for some + * virtual link to give it a pending message. */ - enum GNUNET_NetworkType nt; + int idle; }; /** - * Client connected to the transport service. + * Information we keep for a message that we are reassembling. */ -struct TransportClient +struct ReassemblyContext { - /** - * Kept in a DLL. + * Original message ID for of the message that all the fragments + * belong to. */ - struct TransportClient *next; + struct MessageUUIDP msg_uuid; /** - * Kept in a DLL. + * Which neighbour is this context for? */ - struct TransportClient *prev; + struct Neighbour *neighbour; /** - * Handle to the client. + * Entry in the reassembly heap (sorted by expiration). */ - struct GNUNET_SERVICE_Client *client; + struct GNUNET_CONTAINER_HeapNode *hn; /** - * Message queue to the client. + * Bitfield with @e msg_size bits representing the positions + * where we have received fragments. When we receive a fragment, + * we check the bits in @e bitfield before incrementing @e msg_missing. + * + * Allocated after the reassembled message. */ - struct GNUNET_MQ_Handle *mq; + uint8_t *bitfield; /** - * What type of client is this? + * At what time will we give up reassembly of this message? */ - enum ClientType type; - - union - { + struct GNUNET_TIME_Absolute reassembly_timeout; + /** + * Time we received the last fragment. @e avg_ack_delay must be + * incremented by now - @e last_frag multiplied by @e num_acks. + */ + struct GNUNET_TIME_Absolute last_frag; + + /** + * How big is the message we are reassembling in total? + */ + uint16_t msg_size; + + /** + * How many bytes of the message are still missing? Defragmentation + * is complete when @e msg_missing == 0. + */ + uint16_t msg_missing; + + /* Followed by @e msg_size bytes of the (partially) defragmented original + * message */ + + /* Followed by @e bitfield data */ +}; + + +/** + * A neighbour that at least one communicator is connected to. + */ +struct Neighbour +{ + /** + * Which peer is this about? + */ + struct GNUNET_PeerIdentity pid; + + /** + * Map with `struct ReassemblyContext` structs for fragments under + * reassembly. May be NULL if we currently have no fragments from + * this @e pid (lazy initialization). + */ + struct GNUNET_CONTAINER_MultiHashMap32 *reassembly_map; + + /** + * Heap with `struct ReassemblyContext` structs for fragments under + * reassembly. May be NULL if we currently have no fragments from + * this @e pid (lazy initialization). + */ + struct GNUNET_CONTAINER_Heap *reassembly_heap; + + /** + * Task to free old entries from the @e reassembly_heap and @e reassembly_map. + */ + struct GNUNET_SCHEDULER_Task *reassembly_timeout_task; + + /** + * Head of MDLL of DV hops that have this neighbour as next hop. Must be + * purged if this neighbour goes down. + */ + struct DistanceVectorHop *dv_head; + + /** + * Tail of MDLL of DV hops that have this neighbour as next hop. Must be + * purged if this neighbour goes down. + */ + struct DistanceVectorHop *dv_tail; + + /** + * Head of DLL of queues to this peer. + */ + struct Queue *queue_head; + + /** + * Tail of DLL of queues to this peer. + */ + struct Queue *queue_tail; + + /** + * Handle for an operation to fetch @e last_dv_learn_monotime information from + * the PEERSTORE, or NULL. + */ + struct GNUNET_PEERSTORE_IterateContext *get; + + /** + * Handle to a PEERSTORE store operation to store this @e pid's @e + * @e last_dv_learn_monotime. NULL if no PEERSTORE operation is pending. + */ + struct GNUNET_PEERSTORE_StoreContext *sc; + + /** + * Do we have a confirmed working queue and are thus visible to + * CORE? If so, this is the virtual link, otherwise NULL. + */ + struct VirtualLink *vl; + + /** + * Latest DVLearn monotonic time seen from this peer. Initialized only + * if @e dl_monotime_available is #GNUNET_YES. + */ + struct GNUNET_TIME_Absolute last_dv_learn_monotime; + + /** + * Do we have the lastest value for @e last_dv_learn_monotime from + * PEERSTORE yet, or are we still waiting for a reply of PEERSTORE? + */ + int dv_monotime_available; +}; + + +/** + * Another peer attempted to talk to us, we should try to establish + * a connection in the other direction. + */ +struct IncomingRequest +{ + /** + * Kept in a DLL. + */ + struct IncomingRequest *next; + + /** + * Kept in a DLL. + */ + struct IncomingRequest *prev; + + /** + * Handle for watching the peerstore for HELLOs for this peer. + */ + struct GNUNET_PEERSTORE_WatchContext *wc; + + /** + * Which peer is this about? + */ + struct GNUNET_PeerIdentity pid; +}; + + +/** + * A peer that an application (client) would like us to talk to directly. + */ +struct PeerRequest +{ + /** + * Which peer is this about? + */ + struct GNUNET_PeerIdentity pid; + + /** + * Client responsible for the request. + */ + struct TransportClient *tc; + + /** + * Handle for watching the peerstore for HELLOs for this peer. + */ + struct GNUNET_PEERSTORE_WatchContext *wc; + + /** + * What kind of performance preference does this @e tc have? + * + * TODO: use this! + */ + enum GNUNET_MQ_PriorityPreferences pk; + + /** + * How much bandwidth would this @e tc like to see? + */ + struct GNUNET_BANDWIDTH_Value32NBO bw; +}; + + +/** + * Types of different pending messages. + */ +enum PendingMessageType +{ + /** + * Ordinary message received from the CORE service. + */ + PMT_CORE = 0, + + /** + * Fragment box. + */ + PMT_FRAGMENT_BOX = 1, + + /** + * Reliability box. + */ + PMT_RELIABILITY_BOX = 2, + + /** + * Pending message created during #forward_dv_box(). + */ + PMT_DV_BOX = 3 +}; + + +/** + * Transmission request that is awaiting delivery. The original + * transmission requests from CORE may be too big for some queues. + * In this case, a *tree* of fragments is created. At each + * level of the tree, fragments are kept in a DLL ordered by which + * fragment should be sent next (at the head). The tree is searched + * top-down, with the original message at the root. + * + * To select a node for transmission, first it is checked if the + * current node's message fits with the MTU. If it does not, we + * either calculate the next fragment (based on @e frag_off) from the + * current node, or, if all fragments have already been created, + * descend to the @e head_frag. Even though the node was already + * fragmented, the fragment may be too big if the fragment was + * generated for a queue with a larger MTU. In this case, the node + * may be fragmented again, thus creating a tree. + * + * When acknowledgements for fragments are received, the tree + * must be pruned, removing those parts that were already + * acknowledged. When fragments are sent over a reliable + * channel, they can be immediately removed. + * + * If a message is ever fragmented, then the original "full" message + * is never again transmitted (even if it fits below the MTU), and + * only (remaining) fragments are sent. + */ +struct PendingMessage +{ + /** + * Kept in a MDLL of messages for this @a vl. + */ + struct PendingMessage *next_vl; + + /** + * Kept in a MDLL of messages for this @a vl. + */ + struct PendingMessage *prev_vl; + + /** + * Kept in a MDLL of messages from this @a client (if @e pmt is #PMT_CORE) + */ + struct PendingMessage *next_client; + + /** + * Kept in a MDLL of messages from this @a client (if @e pmt is #PMT_CORE) + */ + struct PendingMessage *prev_client; + + /** + * Kept in a MDLL of messages from this @a cpm (if @e pmt is + * #PMT_FRAGMENT_BOx) + */ + struct PendingMessage *next_frag; + + /** + * Kept in a MDLL of messages from this @a cpm (if @e pmt is + * #PMT_FRAGMENT_BOX) + */ + struct PendingMessage *prev_frag; + + /** + * Head of DLL of PAs for this pending message. + */ + struct PendingAcknowledgement *pa_head; + + /** + * Tail of DLL of PAs for this pending message. + */ + struct PendingAcknowledgement *pa_tail; + + /** + * This message, reliability *or* DV-boxed. Only possibly available + * if @e pmt is #PMT_CORE. + */ + struct PendingMessage *bpm; + + /** + * Target of the request (always the ultimate destination!). + */ + struct VirtualLink *vl; + + /** + * Set to non-NULL value if this message is currently being given to a + * communicator and we are awaiting that communicator's acknowledgement. + * Note that we must not retransmit a pending message while we're still + * in the process of giving it to a communicator. If a pending message + * is free'd while this entry is non-NULL, the @e qe reference to us + * should simply be set to NULL. + */ + struct QueueEntry *qe; + + /** + * Client that issued the transmission request, if @e pmt is #PMT_CORE. + */ + struct TransportClient *client; + + /** + * Head of a MDLL of fragments created for this core message. + */ + struct PendingMessage *head_frag; + + /** + * Tail of a MDLL of fragments created for this core message. + */ + struct PendingMessage *tail_frag; + + /** + * Our parent in the fragmentation tree. + */ + struct PendingMessage *frag_parent; + + /** + * At what time should we give up on the transmission (and no longer retry)? + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * What is the earliest time for us to retry transmission of this message? + */ + struct GNUNET_TIME_Absolute next_attempt; + + /** + * UUID to use for this message (used for reassembly of fragments, only + * initialized if @e msg_uuid_set is #GNUNET_YES). + */ + struct MessageUUIDP msg_uuid; + + /** + * UUID we use to identify this message in our logs. + * Generated by incrementing the "logging_uuid_gen". + */ + unsigned long long logging_uuid; + + /** + * Type of the pending message. + */ + enum PendingMessageType pmt; + + /** + * Preferences for this message. + * TODO: actually use this! + */ + enum GNUNET_MQ_PriorityPreferences prefs; + + /** + * Size of the original message. + */ + uint16_t bytes_msg; + + /** + * Offset at which we should generate the next fragment. + */ + uint16_t frag_off; + + /** + * #GNUNET_YES once @e msg_uuid was initialized + */ + int16_t msg_uuid_set; + + /* Followed by @e bytes_msg to transmit */ +}; + + +/** + * Acknowledgement payload. + */ +struct TransportCummulativeAckPayload +{ + /** + * When did we receive the message we are ACKing? Used to calculate + * the delay we introduced by cummulating ACKs. + */ + struct GNUNET_TIME_Absolute receive_time; + + /** + * UUID of a message being acknowledged. + */ + struct AcknowledgementUUIDP ack_uuid; +}; + + +/** + * Data structure in which we track acknowledgements still to + * be sent to the + */ +struct AcknowledgementCummulator +{ + /** + * Target peer for which we are accumulating ACKs here. + */ + struct GNUNET_PeerIdentity target; + + /** + * ACK data being accumulated. Only @e num_acks slots are valid. + */ + struct TransportCummulativeAckPayload ack_uuids[MAX_CUMMULATIVE_ACKS]; + + /** + * Task scheduled either to transmit the cummulative ACK message, + * or to clean up this data structure after extended periods of + * inactivity (if @e num_acks is zero). + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * When is @e task run (only used if @e num_acks is non-zero)? + */ + struct GNUNET_TIME_Absolute min_transmission_time; + + /** + * Counter to produce the `ack_counter` in the `struct + * TransportReliabilityAckMessage`. Allows the receiver to detect + * lost ACK messages. Incremented by @e num_acks upon transmission. + */ + uint32_t ack_counter; + + /** + * Number of entries used in @e ack_uuids. Reset to 0 upon transmission. + */ + unsigned int num_acks; +}; + + +/** + * One of the addresses of this peer. + */ +struct AddressListEntry +{ + /** + * Kept in a DLL. + */ + struct AddressListEntry *next; + + /** + * Kept in a DLL. + */ + struct AddressListEntry *prev; + + /** + * Which communicator provides this address? + */ + struct TransportClient *tc; + + /** + * The actual address. + */ + const char *address; + + /** + * Current context for storing this address in the peerstore. + */ + struct GNUNET_PEERSTORE_StoreContext *sc; + + /** + * Task to periodically do @e st operation. + */ + struct GNUNET_SCHEDULER_Task *st; + + /** + * What is a typical lifetime the communicator expects this + * address to have? (Always from now.) + */ + struct GNUNET_TIME_Relative expiration; + + /** + * Address identifier used by the communicator. + */ + uint32_t aid; + + /** + * Network type offered by this address. + */ + enum GNUNET_NetworkType nt; +}; + + +/** + * Client connected to the transport service. + */ +struct TransportClient +{ + /** + * Kept in a DLL. + */ + struct TransportClient *next; + + /** + * Kept in a DLL. + */ + struct TransportClient *prev; + + /** + * Handle to the client. + */ + struct GNUNET_SERVICE_Client *client; + + /** + * Message queue to the client. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * What type of client is this? + */ + enum ClientType type; + + union + { /** * Information for @e type #CT_CORE. */ struct { - /** * Head of list of messages pending for this client, sorted by * transmission time ("next_attempt" + possibly internal prioritization). @@ -1772,7 +2371,6 @@ struct TransportClient * Tail of list of messages pending for this client. */ struct PendingMessage *pending_msg_tail; - } core; /** @@ -1780,7 +2378,6 @@ struct TransportClient */ struct { - /** * Peer identity to monitor the addresses of. * Zero to monitor all neighbours. Valid if @@ -1792,7 +2389,6 @@ struct TransportClient * Is this a one-shot monitor? */ int one_shot; - } monitor; @@ -1840,7 +2436,6 @@ struct TransportClient * Characteristics of this communicator. */ enum GNUNET_TRANSPORT_CommunicatorCharacteristics cc; - } communicator; /** @@ -1848,15 +2443,12 @@ struct TransportClient */ struct { - /** * Map of requests for peers the given client application would like to * see connections for. Maps from PIDs to `struct PeerRequest`. */ struct GNUNET_CONTAINER_MultiPeerMap *requests; - } application; - } details; }; @@ -1867,7 +2459,6 @@ struct TransportClient */ struct ValidationState { - /** * For which peer is @a address to be validated (or possibly valid)? * Serves as key in the #validation_map. @@ -1936,7 +2527,7 @@ struct ValidationState * (We must not rotate more often as otherwise we may discard valid answers * due to packet losses, latency and reorderings on the network). */ - struct GNUNET_ShortHashCode challenge; + struct ChallengeNonceP challenge; /** * Claimed address of the peer. @@ -1956,6 +2547,13 @@ struct ValidationState */ struct GNUNET_PEERSTORE_StoreContext *sc; + /** + * Self-imposed limit on the previous flow control window. (May be zero, + * if we never used data from the previous window or are establishing the + * connection for the first time). + */ + uint32_t last_window_consum_limit; + /** * We are technically ready to send the challenge, but we are waiting for * the respective queue to become available for transmission. @@ -1964,6 +2562,65 @@ struct ValidationState }; +/** + * A Backtalker is a peer sending us backchannel messages. We use this + * struct to detect monotonic time violations, cache ephemeral key + * material (to avoid repeatedly checking signatures), and to synchronize + * monotonic time with the PEERSTORE. + */ +struct Backtalker +{ + /** + * Peer this is about. + */ + struct GNUNET_PeerIdentity pid; + + /** + * Last (valid) monotonic time received from this sender. + */ + struct GNUNET_TIME_Absolute monotonic_time; + + /** + * When will this entry time out? + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Last (valid) ephemeral key received from this sender. + */ + struct GNUNET_CRYPTO_EcdhePublicKey last_ephemeral; + + /** + * Task associated with this backtalker. Can be for timeout, + * or other asynchronous operations. + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * Communicator context waiting on this backchannel's @e get, or NULL. + */ + struct CommunicatorMessageContext *cmc; + + /** + * Handle for an operation to fetch @e monotonic_time information from the + * PEERSTORE, or NULL. + */ + struct GNUNET_PEERSTORE_IterateContext *get; + + /** + * Handle to a PEERSTORE store operation for this @e pid's @e + * monotonic_time. NULL if no PEERSTORE operation is pending. + */ + struct GNUNET_PEERSTORE_StoreContext *sc; + + /** + * Number of bytes of the original message body that follows after this + * struct. + */ + size_t body_size; +}; + + /** * Head of linked list of all clients to this service. */ @@ -1987,100 +2644,328 @@ static const struct GNUNET_CONFIGURATION_Handle *GST_cfg; /** * Our public key. */ -static struct GNUNET_PeerIdentity GST_my_identity; +static struct GNUNET_PeerIdentity GST_my_identity; + +/** + * Our private key. + */ +static struct GNUNET_CRYPTO_EddsaPrivateKey *GST_my_private_key; + +/** + * Map from PIDs to `struct Neighbour` entries. A peer is + * a neighbour if we have an MQ to it from some communicator. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *neighbours; + +/** + * Map from PIDs to `struct Backtalker` entries. A peer is + * a backtalker if it recently send us backchannel messages. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *backtalkers; + +/** + * Map from PIDs to `struct AcknowledgementCummulator`s. + * Here we track the cummulative ACKs for transmission. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *ack_cummulators; + +/** + * Map of pending acknowledgements, mapping `struct AcknowledgementUUID` to + * a `struct PendingAcknowledgement`. + */ +static struct GNUNET_CONTAINER_MultiUuidmap *pending_acks; + +/** + * Map from PIDs to `struct DistanceVector` entries describing + * known paths to the peer. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *dv_routes; + +/** + * Map from PIDs to `struct ValidationState` entries describing + * addresses we are aware of and their validity state. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *validation_map; + +/** + * Map from PIDs to `struct VirtualLink` entries describing + * links CORE knows to exist. + */ +static struct GNUNET_CONTAINER_MultiPeerMap *links; + +/** + * Map from challenges to `struct LearnLaunchEntry` values. + */ +static struct GNUNET_CONTAINER_MultiShortmap *dvlearn_map; + +/** + * Head of a DLL sorted by launch time. + */ +static struct LearnLaunchEntry *lle_head; + +/** + * Tail of a DLL sorted by launch time. + */ +static struct LearnLaunchEntry *lle_tail; + +/** + * MIN Heap sorted by "next_challenge" to `struct ValidationState` entries + * sorting addresses we are aware of by when we should next try to (re)validate + * (or expire) them. + */ +static struct GNUNET_CONTAINER_Heap *validation_heap; + +/** + * Database for peer's HELLOs. + */ +static struct GNUNET_PEERSTORE_Handle *peerstore; + +/** + * Task run to initiate DV learning. + */ +static struct GNUNET_SCHEDULER_Task *dvlearn_task; /** - * Our private key. + * Task to run address validation. */ -static struct GNUNET_CRYPTO_EddsaPrivateKey *GST_my_private_key; +static struct GNUNET_SCHEDULER_Task *validation_task; /** - * Map from PIDs to `struct Neighbour` entries. A peer is - * a neighbour if we have an MQ to it from some communicator. + * The most recent PA we have created, head of DLL. + * The length of the DLL is kept in #pa_count. */ -static struct GNUNET_CONTAINER_MultiPeerMap *neighbours; +static struct PendingAcknowledgement *pa_head; /** - * Map from PIDs to `struct DistanceVector` entries describing - * known paths to the peer. + * The oldest PA we have created, tail of DLL. + * The length of the DLL is kept in #pa_count. */ -static struct GNUNET_CONTAINER_MultiPeerMap *dv_routes; +static struct PendingAcknowledgement *pa_tail; /** - * Map from PIDs to `struct ValidationState` entries describing - * addresses we are aware of and their validity state. + * List of incomming connections where we are trying + * to get a connection back established. Length + * kept in #ir_total. */ -static struct GNUNET_CONTAINER_MultiPeerMap *validation_map; +static struct IncomingRequest *ir_head; /** - * Map from challenges to `struct LearnLaunchEntry` values. + * Tail of DLL starting at #ir_head. */ -static struct GNUNET_CONTAINER_MultiShortmap *dvlearn_map; +static struct IncomingRequest *ir_tail; /** - * Head of a DLL sorted by launch time. + * Length of the DLL starting at #ir_head. */ -static struct LearnLaunchEntry *lle_head; +static unsigned int ir_total; /** - * Tail of a DLL sorted by launch time. + * Generator of `logging_uuid` in `struct PendingMessage`. */ -static struct LearnLaunchEntry *lle_tail; +static unsigned long long logging_uuid_gen; /** - * MIN Heap sorted by "next_challenge" to `struct ValidationState` entries - * sorting addresses we are aware of by when we should next try to (re)validate - * (or expire) them. + * Number of entries in the #pa_head/#pa_tail DLL. Used to + * limit the size of the data structure. */ -static struct GNUNET_CONTAINER_Heap *validation_heap; +static unsigned int pa_count; /** - * Database for peer's HELLOs. + * Monotonic time we use for HELLOs generated at this time. TODO: we + * should increase this value from time to time (i.e. whenever a + * `struct AddressListEntry` actually expires), but IF we do this, we + * must also update *all* (remaining) addresses in the PEERSTORE at + * that time! (So for now only increased when the peer is restarted, + * which hopefully roughly matches whenever our addresses change.) */ -static struct GNUNET_PEERSTORE_Handle *peerstore; +static struct GNUNET_TIME_Absolute hello_mono_time; + /** - * Heap sorting `struct EphemeralCacheEntry` by their - * key/signature validity. + * Get an offset into the transmission history buffer for `struct + * PerformanceData`. Note that the caller must perform the required + * modulo #GOODPUT_AGING_SLOTS operation before indexing into the + * array! + * + * An 'age' lasts 15 minute slots. + * + * @return current age of the world */ -static struct GNUNET_CONTAINER_Heap *ephemeral_heap; +static unsigned int +get_age () +{ + struct GNUNET_TIME_Absolute now; + + now = GNUNET_TIME_absolute_get (); + return now.abs_value_us / GNUNET_TIME_UNIT_MINUTES.rel_value_us / 15; +} + /** - * Hash map for looking up `struct EphemeralCacheEntry`s - * by peer identity. (We may have ephemerals in our - * cache for which we do not have a neighbour entry, - * and similar many neighbours may not need ephemerals, - * so we use a second map.) + * Release @a ir data structure. + * + * @param ir data structure to release */ -static struct GNUNET_CONTAINER_MultiPeerMap *ephemeral_map; +static void +free_incoming_request (struct IncomingRequest *ir) +{ + GNUNET_CONTAINER_DLL_remove (ir_head, ir_tail, ir); + GNUNET_assert (ir_total > 0); + ir_total--; + GNUNET_PEERSTORE_watch_cancel (ir->wc); + GNUNET_free (ir); +} + /** - * Task to free expired ephemerals. + * Release @a pa data structure. + * + * @param pa data structure to release */ -static struct GNUNET_SCHEDULER_Task *ephemeral_task; +static void +free_pending_acknowledgement (struct PendingAcknowledgement *pa) +{ + struct Queue *q = pa->queue; + struct PendingMessage *pm = pa->pm; + struct DistanceVectorHop *dvh = pa->dvh; + + GNUNET_CONTAINER_MDLL_remove (pa, pa_head, pa_tail, pa); + pa_count--; + if (NULL != q) + { + GNUNET_CONTAINER_MDLL_remove (queue, q->pa_head, q->pa_tail, pa); + pa->queue = NULL; + } + if (NULL != pm) + { + GNUNET_CONTAINER_MDLL_remove (pm, pm->pa_head, pm->pa_tail, pa); + pa->pm = NULL; + } + if (NULL != dvh) + { + GNUNET_CONTAINER_MDLL_remove (dvh, dvh->pa_head, dvh->pa_tail, pa); + pa->queue = NULL; + } + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multiuuidmap_remove (pending_acks, + &pa->ack_uuid.value, + pa)); + GNUNET_free (pa); +} + /** - * Task run to initiate DV learning. + * Free fragment tree below @e root, excluding @e root itself. + * FIXME: this does NOT seem to have the intended semantics + * based on how this is called. Seems we generally DO expect + * @a root to be free'ed itself as well! + * + * @param root root of the tree to free */ -static struct GNUNET_SCHEDULER_Task *dvlearn_task; +static void +free_fragment_tree (struct PendingMessage *root) +{ + struct PendingMessage *frag; + + while (NULL != (frag = root->head_frag)) + { + struct PendingAcknowledgement *pa; + + free_fragment_tree (frag); + while (NULL != (pa = frag->pa_head)) + { + GNUNET_CONTAINER_MDLL_remove (pm, frag->pa_head, frag->pa_tail, pa); + pa->pm = NULL; + } + GNUNET_CONTAINER_MDLL_remove (frag, root->head_frag, root->tail_frag, frag); + GNUNET_free (frag); + } +} + /** - * Task to run address validation. + * Release memory associated with @a pm and remove @a pm from associated + * data structures. @a pm must be a top-level pending message and not + * a fragment in the tree. The entire tree is freed (if applicable). + * + * @param pm the pending message to free */ -static struct GNUNET_SCHEDULER_Task *validation_task; +static void +free_pending_message (struct PendingMessage *pm) +{ + struct TransportClient *tc = pm->client; + struct VirtualLink *vl = pm->vl; + struct PendingAcknowledgement *pa; + + if (NULL != tc) + { + GNUNET_CONTAINER_MDLL_remove (client, + tc->details.core.pending_msg_head, + tc->details.core.pending_msg_tail, + pm); + } + if (NULL != vl) + { + GNUNET_CONTAINER_MDLL_remove (vl, + vl->pending_msg_head, + vl->pending_msg_tail, + pm); + } + while (NULL != (pa = pm->pa_head)) + { + GNUNET_CONTAINER_MDLL_remove (pm, pm->pa_head, pm->pa_tail, pa); + pa->pm = NULL; + } + + free_fragment_tree (pm); + if (NULL != pm->qe) + { + GNUNET_assert (pm == pm->qe->pm); + pm->qe->pm = NULL; + } + if (NULL != pm->bpm) + { + free_fragment_tree (pm->bpm); + GNUNET_free (pm->bpm); + } + GNUNET_free (pm); +} /** - * Free cached ephemeral key. + * Free virtual link. * - * @param ece cached signature to free + * @param vl link data to free */ static void -free_ephemeral (struct EphemeralCacheEntry *ece) +free_virtual_link (struct VirtualLink *vl) { - GNUNET_CONTAINER_multipeermap_remove (ephemeral_map, &ece->target, ece); - GNUNET_CONTAINER_heap_remove_node (ece->hn); - GNUNET_free (ece); + struct PendingMessage *pm; + struct CoreSentContext *csc; + + while (NULL != (pm = vl->pending_msg_head)) + free_pending_message (pm); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (links, &vl->target, vl)); + if (NULL != vl->visibility_task) + { + GNUNET_SCHEDULER_cancel (vl->visibility_task); + vl->visibility_task = NULL; + } + if (NULL != vl->fc_retransmit_task) + { + GNUNET_SCHEDULER_cancel (vl->fc_retransmit_task); + vl->fc_retransmit_task = NULL; + } + while (NULL != (csc = vl->csc_head)) + { + GNUNET_CONTAINER_DLL_remove (vl->csc_head, vl->csc_tail, csc); + GNUNET_assert (vl == csc->vl); + csc->vl = NULL; + } + GNUNET_break (NULL == vl->n); + GNUNET_break (NULL == vl->dv); + GNUNET_free (vl); } @@ -2092,7 +2977,9 @@ free_ephemeral (struct EphemeralCacheEntry *ece) static void free_validation_state (struct ValidationState *vs) { - GNUNET_CONTAINER_multipeermap_remove (validation_map, &vs->pid, vs); + GNUNET_assert ( + GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (validation_map, &vs->pid, vs)); GNUNET_CONTAINER_heap_remove_node (vs->hn); vs->hn = NULL; if (NULL != vs->sc) @@ -2106,7 +2993,7 @@ free_validation_state (struct ValidationState *vs) /** - * Lookup neighbour record for peer @a pid. + * Lookup neighbour for peer @a pid. * * @param pid neighbour to look for * @return NULL if we do not have this peer as a neighbour @@ -2118,6 +3005,19 @@ lookup_neighbour (const struct GNUNET_PeerIdentity *pid) } +/** + * Lookup virtual link for peer @a pid. + * + * @param pid virtual link to look for + * @return NULL if we do not have this peer as a virtual link + */ +static struct VirtualLink * +lookup_virtual_link (const struct GNUNET_PeerIdentity *pid) +{ + return GNUNET_CONTAINER_multipeermap_get (links, pid); +} + + /** * Details about what to notify monitors about. */ @@ -2165,13 +3065,54 @@ free_distance_vector_hop (struct DistanceVectorHop *dvh) { struct Neighbour *n = dvh->next_hop; struct DistanceVector *dv = dvh->dv; + struct PendingAcknowledgement *pa; + while (NULL != (pa = dvh->pa_head)) + { + GNUNET_CONTAINER_MDLL_remove (dvh, dvh->pa_head, dvh->pa_tail, pa); + pa->dvh = NULL; + } GNUNET_CONTAINER_MDLL_remove (neighbour, n->dv_head, n->dv_tail, dvh); GNUNET_CONTAINER_MDLL_remove (dv, dv->dv_head, dv->dv_tail, dvh); GNUNET_free (dvh); } +/** + * Task run to check whether the hops of the @a cls still + * are validated, or if we need to core about disconnection. + * + * @param cls a `struct VirtualLink` + */ +static void +check_link_down (void *cls); + + +/** + * Send message to CORE clients that we lost a connection. + * + * @param pid peer the connection was for + */ +static void +cores_send_disconnect_info (const struct GNUNET_PeerIdentity *pid) +{ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Informing CORE clients about disconnect from %s\n", + GNUNET_i2s (pid)); + for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) + { + struct GNUNET_MQ_Envelope *env; + struct DisconnectInfoMessage *dim; + + if (CT_CORE != tc->type) + continue; + env = GNUNET_MQ_msg (dim, GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT); + dim->peer = *pid; + GNUNET_MQ_send (tc->mq, env); + } +} + + /** * Free entry in #dv_routes. First frees all hops to the target, and * if there are no entries left, frees @a dv as well. @@ -2187,13 +3128,33 @@ free_dv_route (struct DistanceVector *dv) free_distance_vector_hop (dvh); if (NULL == dv->dv_head) { + struct VirtualLink *vl; + GNUNET_assert ( GNUNET_YES == GNUNET_CONTAINER_multipeermap_remove (dv_routes, &dv->target, dv)); - if (NULL != dv->visibility_task) - GNUNET_SCHEDULER_cancel (dv->visibility_task); + if (NULL != (vl = dv->vl)) + { + GNUNET_assert (dv == vl->dv); + vl->dv = NULL; + if (NULL == vl->n) + { + cores_send_disconnect_info (&dv->target); + free_virtual_link (vl); + } + else + { + GNUNET_SCHEDULER_cancel (vl->visibility_task); + vl->visibility_task = GNUNET_SCHEDULER_add_now (&check_link_down, vl); + } + dv->vl = NULL; + } + if (NULL != dv->timeout_task) + { GNUNET_SCHEDULER_cancel (dv->timeout_task); + dv->timeout_task = NULL; + } GNUNET_free (dv); } } @@ -2307,9 +3268,9 @@ free_reassembly_context (struct ReassemblyContext *rc) GNUNET_assert (rc == GNUNET_CONTAINER_heap_remove_node (rc->hn)); GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multishortmap_remove (n->reassembly_map, - &rc->msg_uuid, - rc)); + GNUNET_CONTAINER_multihashmap32_remove (n->reassembly_map, + rc->msg_uuid.uuid, + rc)); GNUNET_free (rc); } @@ -2329,7 +3290,7 @@ reassembly_cleanup_task (void *cls) while (NULL != (rc = GNUNET_CONTAINER_heap_peek (n->reassembly_heap))) { if (0 == GNUNET_TIME_absolute_get_remaining (rc->reassembly_timeout) - .rel_value_us) + .rel_value_us) { free_reassembly_context (rc); continue; @@ -2353,9 +3314,7 @@ reassembly_cleanup_task (void *cls) * @return #GNUNET_OK (continue iteration) */ static int -free_reassembly_cb (void *cls, - const struct GNUNET_ShortHashCode *key, - void *value) +free_reassembly_cb (void *cls, uint32_t key, void *value) { struct ReassemblyContext *rc = value; @@ -2375,20 +3334,19 @@ static void free_neighbour (struct Neighbour *neighbour) { struct DistanceVectorHop *dvh; + struct VirtualLink *vl; GNUNET_assert (NULL == neighbour->queue_head); GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multipeermap_remove (neighbours, &neighbour->pid, neighbour)); - if (NULL != neighbour->timeout_task) - GNUNET_SCHEDULER_cancel (neighbour->timeout_task); if (NULL != neighbour->reassembly_map) { - GNUNET_CONTAINER_multishortmap_iterate (neighbour->reassembly_map, - &free_reassembly_cb, - NULL); - GNUNET_CONTAINER_multishortmap_destroy (neighbour->reassembly_map); + GNUNET_CONTAINER_multihashmap32_iterate (neighbour->reassembly_map, + &free_reassembly_cb, + NULL); + GNUNET_CONTAINER_multihashmap32_destroy (neighbour->reassembly_map); neighbour->reassembly_map = NULL; GNUNET_CONTAINER_heap_destroy (neighbour->reassembly_heap); neighbour->reassembly_heap = NULL; @@ -2402,7 +3360,36 @@ free_neighbour (struct Neighbour *neighbour) free_dv_route (dv); } if (NULL != neighbour->reassembly_timeout_task) + { GNUNET_SCHEDULER_cancel (neighbour->reassembly_timeout_task); + neighbour->reassembly_timeout_task = NULL; + } + if (NULL != neighbour->get) + { + GNUNET_PEERSTORE_iterate_cancel (neighbour->get); + neighbour->get = NULL; + } + if (NULL != neighbour->sc) + { + GNUNET_PEERSTORE_store_cancel (neighbour->sc); + neighbour->sc = NULL; + } + if (NULL != (vl = neighbour->vl)) + { + GNUNET_assert (neighbour == vl->n); + vl->n = NULL; + if (NULL == vl->dv) + { + cores_send_disconnect_info (&vl->target); + free_virtual_link (vl); + } + else + { + GNUNET_SCHEDULER_cancel (vl->visibility_task); + vl->visibility_task = GNUNET_SCHEDULER_add_now (&check_link_down, vl); + } + neighbour->vl = NULL; + } GNUNET_free (neighbour); } @@ -2412,19 +3399,16 @@ free_neighbour (struct Neighbour *neighbour) * * @param tc client to inform (must be CORE client) * @param pid peer the connection is for - * @param quota_out current quota for the peer */ static void core_send_connect_info (struct TransportClient *tc, - const struct GNUNET_PeerIdentity *pid, - struct GNUNET_BANDWIDTH_Value32NBO quota_out) + const struct GNUNET_PeerIdentity *pid) { struct GNUNET_MQ_Envelope *env; struct ConnectInfoMessage *cim; GNUNET_assert (CT_CORE == tc->type); env = GNUNET_MQ_msg (cim, GNUNET_MESSAGE_TYPE_TRANSPORT_CONNECT); - cim->quota_out = quota_out; cim->id = *pid; GNUNET_MQ_send (tc->mq, env); } @@ -2434,48 +3418,26 @@ core_send_connect_info (struct TransportClient *tc, * Send message to CORE clients that we gained a connection * * @param pid peer the queue was for - * @param quota_out current quota for the peer - */ -static void -cores_send_connect_info (const struct GNUNET_PeerIdentity *pid, - struct GNUNET_BANDWIDTH_Value32NBO quota_out) -{ - for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) - { - if (CT_CORE != tc->type) - continue; - core_send_connect_info (tc, pid, quota_out); - } -} - - -/** - * Send message to CORE clients that we lost a connection. - * - * @param pid peer the connection was for */ static void -cores_send_disconnect_info (const struct GNUNET_PeerIdentity *pid) +cores_send_connect_info (const struct GNUNET_PeerIdentity *pid) { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Informing CORE clients about connection to %s\n", + GNUNET_i2s (pid)); for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) { - struct GNUNET_MQ_Envelope *env; - struct DisconnectInfoMessage *dim; - if (CT_CORE != tc->type) continue; - env = GNUNET_MQ_msg (dim, GNUNET_MESSAGE_TYPE_TRANSPORT_DISCONNECT); - dim->peer = *pid; - GNUNET_MQ_send (tc->mq, env); + core_send_connect_info (tc, pid); } } /** - * We believe we are ready to transmit a message on a queue. Double-checks - * with the queue's "tracker_out" and then gives the message to the - * communicator for transmission (updating the tracker, and re-scheduling - * itself if applicable). + * We believe we are ready to transmit a message on a queue. Gives the + * message to the communicator for transmission (updating the tracker, + * and re-scheduling itself if applicable). * * @param cls the `struct Queue` to process transmissions for */ @@ -2484,26 +3446,16 @@ transmit_on_queue (void *cls); /** - * Schedule next run of #transmit_on_queue(). Does NOTHING if - * we should run immediately or if the message queue is empty. - * Test for no task being added AND queue not being empty to - * transmit immediately afterwards! This function must only - * be called if the message queue is non-empty! + * Called whenever something changed that might effect when we + * try to do the next transmission on @a queue using #transmit_on_queue(). * * @param queue the queue to do scheduling for - * @param inside_job set to #GNUNET_YES if called from - * #transmit_on_queue() itself and NOT setting - * the task means running immediately + * @param p task priority to use, if @a queue is scheduled */ static void -schedule_transmit_on_queue (struct Queue *queue, int inside_job) +schedule_transmit_on_queue (struct Queue *queue, + enum GNUNET_SCHEDULER_Priority p) { - struct Neighbour *n = queue->neighbour; - struct PendingMessage *pm = n->pending_msg_head; - struct GNUNET_TIME_Relative out_delay; - unsigned int wsize; - - GNUNET_assert (NULL != pm); if (queue->tc->details.communicator.total_queue_length >= COMMUNICATOR_TOTAL_QUEUE_LIMIT) { @@ -2512,6 +3464,7 @@ schedule_transmit_on_queue (struct Queue *queue, int inside_job) "# Transmission throttled due to communicator queue limit", 1, GNUNET_NO); + queue->idle = GNUNET_NO; return; } if (queue->queue_length >= QUEUE_LENGTH_LIMIT) @@ -2520,41 +3473,65 @@ schedule_transmit_on_queue (struct Queue *queue, int inside_job) "# Transmission throttled due to queue queue limit", 1, GNUNET_NO); + queue->idle = GNUNET_NO; return; } - - wsize = (0 == queue->mtu) ? pm->bytes_msg /* FIXME: add overheads? */ - : queue->mtu; - out_delay = GNUNET_BANDWIDTH_tracker_get_delay (&queue->tracker_out, wsize); - out_delay = GNUNET_TIME_relative_max (GNUNET_TIME_absolute_get_remaining ( - pm->next_attempt), - out_delay); - if ((GNUNET_YES == inside_job) && (0 == out_delay.rel_value_us)) - return; /* we should run immediately! */ - /* queue has changed since we were scheduled, reschedule again */ + /* queue might indeed be ready, schedule it */ + if (NULL != queue->transmit_task) + GNUNET_SCHEDULER_cancel (queue->transmit_task); queue->transmit_task = - GNUNET_SCHEDULER_add_delayed (out_delay, &transmit_on_queue, queue); - if (out_delay.rel_value_us > DELAY_WARN_THRESHOLD.rel_value_us) - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Next transmission on queue `%s' in %s (high delay)\n", - queue->address, - GNUNET_STRINGS_relative_time_to_string (out_delay, GNUNET_YES)); - else - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Next transmission on queue `%s' in %s\n", - queue->address, - GNUNET_STRINGS_relative_time_to_string (out_delay, GNUNET_YES)); + GNUNET_SCHEDULER_add_with_priority (p, &transmit_on_queue, queue); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Considering transmission on queue `%s' to %s\n", + queue->address, + GNUNET_i2s (&queue->neighbour->pid)); } /** - * Check whether the CORE visibility of @a n changed. If so, - * check whether we need to notify CORE. + * Task run to check whether the hops of the @a cls still + * are validated, or if we need to core about disconnection. * - * @param n neighbour to perform the check for + * @param cls a `struct VirtualLink` */ static void -update_neighbour_core_visibility (struct Neighbour *n); +check_link_down (void *cls) +{ + struct VirtualLink *vl = cls; + struct DistanceVector *dv = vl->dv; + struct Neighbour *n = vl->n; + struct GNUNET_TIME_Absolute dvh_timeout; + struct GNUNET_TIME_Absolute q_timeout; + + vl->visibility_task = NULL; + dvh_timeout = GNUNET_TIME_UNIT_ZERO_ABS; + for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; + pos = pos->next_dv) + dvh_timeout = GNUNET_TIME_absolute_max (dvh_timeout, pos->path_valid_until); + if (0 == GNUNET_TIME_absolute_get_remaining (dvh_timeout).rel_value_us) + { + vl->dv->vl = NULL; + vl->dv = NULL; + } + q_timeout = GNUNET_TIME_UNIT_ZERO_ABS; + for (struct Queue *q = n->queue_head; NULL != q; q = q->next_neighbour) + q_timeout = GNUNET_TIME_absolute_max (q_timeout, q->validated_until); + if (0 == GNUNET_TIME_absolute_get_remaining (q_timeout).rel_value_us) + { + vl->n->vl = NULL; + vl->n = NULL; + } + if ((NULL == vl->n) && (NULL == vl->dv)) + { + cores_send_disconnect_info (&vl->target); + free_virtual_link (vl); + return; + } + vl->visibility_task = + GNUNET_SCHEDULER_add_at (GNUNET_TIME_absolute_max (q_timeout, dvh_timeout), + &check_link_down, + vl); +} /** @@ -2567,21 +3544,24 @@ free_queue (struct Queue *queue) { struct Neighbour *neighbour = queue->neighbour; struct TransportClient *tc = queue->tc; - struct MonitorEvent me = {.cs = GNUNET_TRANSPORT_CS_DOWN, - .rtt = GNUNET_TIME_UNIT_FOREVER_REL}; + struct MonitorEvent me = { .cs = GNUNET_TRANSPORT_CS_DOWN, + .rtt = GNUNET_TIME_UNIT_FOREVER_REL }; struct QueueEntry *qe; int maxxed; + struct PendingAcknowledgement *pa; + struct VirtualLink *vl; if (NULL != queue->transmit_task) { GNUNET_SCHEDULER_cancel (queue->transmit_task); queue->transmit_task = NULL; } - if (NULL != queue->visibility_task) + while (NULL != (pa = queue->pa_head)) { - GNUNET_SCHEDULER_cancel (queue->visibility_task); - queue->visibility_task = NULL; + GNUNET_CONTAINER_MDLL_remove (queue, queue->pa_head, queue->pa_tail, pa); + pa->queue = NULL; } + GNUNET_CONTAINER_MDLL_remove (neighbour, neighbour->queue_head, neighbour->queue_tail, @@ -2608,7 +3588,7 @@ free_queue (struct Queue *queue) if ((maxxed) && (COMMUNICATOR_TOTAL_QUEUE_LIMIT < tc->details.communicator.total_queue_length)) { - /* Communicator dropped below threshold, resume all queues */ + /* Communicator dropped below threshold, resume all _other_ queues */ GNUNET_STATISTICS_update ( GST_stats, "# Transmission throttled due to communicator queue limit", @@ -2616,16 +3596,17 @@ free_queue (struct Queue *queue) GNUNET_NO); for (struct Queue *s = tc->details.communicator.queue_head; NULL != s; s = s->next_client) - schedule_transmit_on_queue (s, GNUNET_NO); + schedule_transmit_on_queue (s, GNUNET_SCHEDULER_PRIORITY_DEFAULT); } notify_monitors (&neighbour->pid, queue->address, queue->nt, &me); - GNUNET_BANDWIDTH_tracker_notification_stop (&queue->tracker_in); - GNUNET_BANDWIDTH_tracker_notification_stop (&queue->tracker_out); GNUNET_free (queue); - update_neighbour_core_visibility (neighbour); - cores_send_disconnect_info (&neighbour->pid); - + vl = lookup_virtual_link (&neighbour->pid); + if ((NULL != vl) && (neighbour == vl->n)) + { + GNUNET_SCHEDULER_cancel (vl->visibility_task); + check_link_down (vl); + } if (NULL == neighbour->queue_head) { free_neighbour (neighbour); @@ -2704,6 +3685,7 @@ client_disconnect_cb (void *cls, struct TransportClient *tc = app_ctx; (void) cls; + (void) client; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client %p disconnected, cleaning up.\n", tc); @@ -2712,32 +3694,36 @@ client_disconnect_cb (void *cls, { case CT_NONE: break; + case CT_CORE: { - struct PendingMessage *pm; + struct PendingMessage *pm; - while (NULL != (pm = tc->details.core.pending_msg_head)) - { - GNUNET_CONTAINER_MDLL_remove (client, - tc->details.core.pending_msg_head, - tc->details.core.pending_msg_tail, - pm); - pm->client = NULL; + while (NULL != (pm = tc->details.core.pending_msg_head)) + { + GNUNET_CONTAINER_MDLL_remove (client, + tc->details.core.pending_msg_head, + tc->details.core.pending_msg_tail, + pm); + pm->client = NULL; + } } - } - break; + break; + case CT_MONITOR: break; + case CT_COMMUNICATOR: { - struct Queue *q; - struct AddressListEntry *ale; + struct Queue *q; + struct AddressListEntry *ale; + + while (NULL != (q = tc->details.communicator.queue_head)) + free_queue (q); + while (NULL != (ale = tc->details.communicator.addr_head)) + free_address_list_entry (ale); + GNUNET_free (tc->details.communicator.address_prefix); + } + break; - while (NULL != (q = tc->details.communicator.queue_head)) - free_queue (q); - while (NULL != (ale = tc->details.communicator.addr_head)) - free_address_list_entry (ale); - GNUNET_free (tc->details.communicator.address_prefix); - } - break; case CT_APPLICATION: GNUNET_CONTAINER_multipeermap_iterate (tc->details.application.requests, &stop_peer_request, @@ -2764,9 +3750,12 @@ notify_client_connect_info (void *cls, void *value) { struct TransportClient *tc = cls; - struct Neighbour *neighbour = value; - core_send_connect_info (tc, pid, neighbour->quota_out); + (void) value; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Telling new CORE client about existing connection to %s\n", + GNUNET_i2s (pid)); + core_send_connect_info (tc, pid); return GNUNET_OK; } @@ -2801,6 +3790,9 @@ handle_client_start (void *cls, const struct StartMessage *start) return; } tc->type = CT_CORE; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New CORE client with PID %s registered\n", + GNUNET_i2s (&start->self)); GNUNET_CONTAINER_multipeermap_iterate (neighbours, ¬ify_client_connect_info, tc); @@ -2826,8 +3818,8 @@ check_client_send (void *cls, const struct OutboundMessage *obm) GNUNET_break (0); return GNUNET_SYSERR; } - size = ntohs (obm->header.size) - sizeof (struct OutboundMessage); - if (size < sizeof (struct GNUNET_MessageHeader)) + size = ntohs (obm->header.size) - sizeof(struct OutboundMessage); + if (size < sizeof(struct GNUNET_MessageHeader)) { GNUNET_break (0); return GNUNET_SYSERR; @@ -2843,238 +3835,210 @@ check_client_send (void *cls, const struct OutboundMessage *obm) /** - * Free fragment tree below @e root, excluding @e root itself. + * Send a response to the @a pm that we have processed a "send" + * request. Sends a confirmation to the "core" client responsible for + * the original request and free's @a pm. * - * @param root root of the tree to free + * @param pm handle to the original pending message */ static void -free_fragment_tree (struct PendingMessage *root) +client_send_response (struct PendingMessage *pm) { - struct PendingMessage *frag; + struct TransportClient *tc = pm->client; + struct VirtualLink *vl = pm->vl; - while (NULL != (frag = root->head_frag)) + if (NULL != tc) { - free_fragment_tree (frag); - GNUNET_CONTAINER_MDLL_remove (frag, root->head_frag, root->tail_frag, frag); - GNUNET_free (frag); + struct GNUNET_MQ_Envelope *env; + struct SendOkMessage *som; + + env = GNUNET_MQ_msg (som, GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK); + som->peer = vl->target; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Confirming transmission of <%llu> to %s\n", + pm->logging_uuid, + GNUNET_i2s (&vl->target)); + GNUNET_MQ_send (tc->mq, env); } + free_pending_message (pm); } /** - * Release memory associated with @a pm and remove @a pm from associated - * data structures. @a pm must be a top-level pending message and not - * a fragment in the tree. The entire tree is freed (if applicable). + * Pick @a hops_array_length random DV paths satisfying @a options * - * @param pm the pending message to free + * @param dv data structure to pick paths from + * @param options constraints to satisfy + * @param hops_array[out] set to the result + * @param hops_array_length length of the @a hops_array + * @return number of entries set in @a hops_array */ -static void -free_pending_message (struct PendingMessage *pm) +static unsigned int +pick_random_dv_hops (const struct DistanceVector *dv, + enum RouteMessageOptions options, + struct DistanceVectorHop **hops_array, + unsigned int hops_array_length) { - struct TransportClient *tc = pm->client; - struct Neighbour *target = pm->target; + uint64_t choices[hops_array_length]; + uint64_t num_dv; + unsigned int dv_count; - if (NULL != tc) + /* Pick random vectors, but weighted by distance, giving more weight + to shorter vectors */ + num_dv = 0; + dv_count = 0; + for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; + pos = pos->next_dv) { - GNUNET_CONTAINER_MDLL_remove (client, - tc->details.core.pending_msg_head, - tc->details.core.pending_msg_tail, - pm); + if ((0 == (options & RMO_UNCONFIRMED_ALLOWED)) && + (GNUNET_TIME_absolute_get_remaining (pos->path_valid_until) + .rel_value_us == 0)) + continue; /* pos unconfirmed and confirmed required */ + num_dv += MAX_DV_HOPS_ALLOWED - pos->distance; + dv_count++; } - GNUNET_CONTAINER_MDLL_remove (neighbour, - target->pending_msg_head, - target->pending_msg_tail, - pm); - free_fragment_tree (pm); - if (NULL != pm->qe) + if (0 == dv_count) + return 0; + if (dv_count <= hops_array_length) { - GNUNET_assert (pm == pm->qe->pm); - pm->qe->pm = NULL; + dv_count = 0; + for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; + pos = pos->next_dv) + hops_array[dv_count++] = pos; + return dv_count; } - GNUNET_free_non_null (pm->bpm); - GNUNET_free (pm); + for (unsigned int i = 0; i < hops_array_length; i++) + { + int ok = GNUNET_NO; + while (GNUNET_NO == ok) + { + choices[i] = + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, num_dv); + ok = GNUNET_YES; + for (unsigned int j = 0; j < i; j++) + if (choices[i] == choices[j]) + { + ok = GNUNET_NO; + break; + } + } + } + dv_count = 0; + num_dv = 0; + for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; + pos = pos->next_dv) + { + uint32_t delta = MAX_DV_HOPS_ALLOWED - pos->distance; + + if ((0 == (options & RMO_UNCONFIRMED_ALLOWED)) && + (GNUNET_TIME_absolute_get_remaining (pos->path_valid_until) + .rel_value_us == 0)) + continue; /* pos unconfirmed and confirmed required */ + for (unsigned int i = 0; i < hops_array_length; i++) + if ((num_dv <= choices[i]) && (num_dv + delta > choices[i])) + hops_array[dv_count++] = pos; + num_dv += delta; + } + return dv_count; } /** - * Send a response to the @a pm that we have processed a - * "send" request with status @a success. We - * transmitted @a bytes_physical on the actual wire. - * Sends a confirmation to the "core" client responsible - * for the original request and free's @a pm. + * Communicator started. Test message is well-formed. * - * @param pm handle to the original pending message - * @param success status code, #GNUNET_OK on success, #GNUNET_SYSERR - * for transmission failure - * @param bytes_physical amount of bandwidth consumed + * @param cls the client + * @param cam the send message that was sent */ -static void -client_send_response (struct PendingMessage *pm, - int success, - uint32_t bytes_physical) +static int +check_communicator_available ( + void *cls, + const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *cam) { - struct TransportClient *tc = pm->client; - struct Neighbour *target = pm->target; - struct GNUNET_MQ_Envelope *env; - struct SendOkMessage *som; + struct TransportClient *tc = cls; + uint16_t size; - if (NULL != tc) + if (CT_NONE != tc->type) { - env = GNUNET_MQ_msg (som, GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK); - som->success = htonl ((uint32_t) success); - som->bytes_msg = htons (pm->bytes_msg); - som->bytes_physical = htonl (bytes_physical); - som->peer = target->pid; - GNUNET_MQ_send (tc->mq, env); + GNUNET_break (0); + return GNUNET_SYSERR; } - free_pending_message (pm); + tc->type = CT_COMMUNICATOR; + size = ntohs (cam->header.size) - sizeof(*cam); + if (0 == size) + return GNUNET_OK; /* receive-only communicator */ + GNUNET_MQ_check_zero_termination (cam); + return GNUNET_OK; } /** - * Checks the message queue for a neighbour for messages that have timed - * out and purges them. + * Send ACK to communicator (if requested) and free @a cmc. * - * @param cls a `struct Neighbour` + * @param cmc context for which we are done handling the message */ static void -check_queue_timeouts (void *cls) +finish_cmc_handling (struct CommunicatorMessageContext *cmc) { - struct Neighbour *n = cls; - struct PendingMessage *pm; - struct GNUNET_TIME_Absolute now; - struct GNUNET_TIME_Absolute earliest_timeout; - - n->timeout_task = NULL; - earliest_timeout = GNUNET_TIME_UNIT_FOREVER_ABS; - now = GNUNET_TIME_absolute_get (); - for (struct PendingMessage *pos = n->pending_msg_head; NULL != pos; pos = pm) + if (0 != ntohl (cmc->im.fc_on)) { - pm = pos->next_neighbour; - if (pos->timeout.abs_value_us <= now.abs_value_us) - { - GNUNET_STATISTICS_update (GST_stats, - "# messages dropped (timeout before confirmation)", - 1, - GNUNET_NO); - client_send_response (pm, GNUNET_NO, 0); - continue; - } - earliest_timeout = - GNUNET_TIME_absolute_min (earliest_timeout, pos->timeout); + /* send ACK when done to communicator for flow control! */ + struct GNUNET_MQ_Envelope *env; + struct GNUNET_TRANSPORT_IncomingMessageAck *ack; + + env = GNUNET_MQ_msg (ack, GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG_ACK); + ack->reserved = htonl (0); + ack->fc_id = cmc->im.fc_id; + ack->sender = cmc->im.sender; + GNUNET_MQ_send (cmc->tc->mq, env); } - n->earliest_timeout = earliest_timeout; - if (NULL != n->pending_msg_head) - n->timeout_task = - GNUNET_SCHEDULER_add_at (earliest_timeout, &check_queue_timeouts, n); + GNUNET_SERVICE_client_continue (cmc->tc->client); + GNUNET_free (cmc); } /** - * Client asked for transmission to a peer. Process the request. + * Client confirms that it is done handling message(s) to a particular + * peer. We may now provide more messages to CORE for this peer. + * + * Notifies the respective queues that more messages can now be received. * * @param cls the client - * @param obm the send message that was sent + * @param rom the message that was sent */ static void -handle_client_send (void *cls, const struct OutboundMessage *obm) +handle_client_recv_ok (void *cls, const struct RecvOkMessage *rom) { struct TransportClient *tc = cls; - struct PendingMessage *pm; - const struct GNUNET_MessageHeader *obmm; - struct Neighbour *target; - uint32_t bytes_msg; - int was_empty; + struct VirtualLink *vl; + uint32_t delta; + struct CommunicatorMessageContext *cmc; - GNUNET_assert (CT_CORE == tc->type); - obmm = (const struct GNUNET_MessageHeader *) &obm[1]; - bytes_msg = ntohs (obmm->size); - target = lookup_neighbour (&obm->peer); - if (NULL == target) + if (CT_CORE != tc->type) + { + GNUNET_break (0); + GNUNET_SERVICE_client_drop (tc->client); + return; + } + vl = lookup_virtual_link (&rom->peer); + if (NULL == vl) { - /* Failure: don't have this peer as a neighbour (anymore). - Might have gone down asynchronously, so this is NOT - a protocol violation by CORE. Still count the event, - as this should be rare. */ - struct GNUNET_MQ_Envelope *env; - struct SendOkMessage *som; - - env = GNUNET_MQ_msg (som, GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_OK); - som->success = htonl (GNUNET_SYSERR); - som->bytes_msg = htonl (bytes_msg); - som->bytes_physical = htonl (0); - som->peer = obm->peer; - GNUNET_MQ_send (tc->mq, env); - GNUNET_SERVICE_client_continue (tc->client); GNUNET_STATISTICS_update (GST_stats, - "# messages dropped (neighbour unknown)", + "# RECV_OK dropped: virtual link unknown", 1, GNUNET_NO); + GNUNET_SERVICE_client_continue (tc->client); return; } - was_empty = (NULL == target->pending_msg_head); - pm = GNUNET_malloc (sizeof (struct PendingMessage) + bytes_msg); - pm->client = tc; - pm->target = target; - pm->bytes_msg = bytes_msg; - pm->timeout = - GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_ntoh (obm->timeout)); - memcpy (&pm[1], &obm[1], bytes_msg); - GNUNET_CONTAINER_MDLL_insert (neighbour, - target->pending_msg_head, - target->pending_msg_tail, - pm); - GNUNET_CONTAINER_MDLL_insert (client, - tc->details.core.pending_msg_head, - tc->details.core.pending_msg_tail, - pm); - if (target->earliest_timeout.abs_value_us > pm->timeout.abs_value_us) - { - target->earliest_timeout.abs_value_us = pm->timeout.abs_value_us; - if (NULL != target->timeout_task) - GNUNET_SCHEDULER_cancel (target->timeout_task); - target->timeout_task = GNUNET_SCHEDULER_add_at (target->earliest_timeout, - &check_queue_timeouts, - target); - } - if (! was_empty) - return; /* all queues must already be busy */ - for (struct Queue *queue = target->queue_head; NULL != queue; - queue = queue->next_neighbour) - { - /* try transmission on any queue that is idle */ - if (NULL == queue->transmit_task) - queue->transmit_task = - GNUNET_SCHEDULER_add_now (&transmit_on_queue, queue); - } -} - - -/** - * Communicator started. Test message is well-formed. - * - * @param cls the client - * @param cam the send message that was sent - */ -static int -check_communicator_available ( - void *cls, - const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *cam) -{ - struct TransportClient *tc = cls; - uint16_t size; - - if (CT_NONE != tc->type) + delta = ntohl (rom->increase_window_delta); + vl->core_recv_window += delta; + if (vl->core_recv_window <= 0) + return; + /* resume communicators */ + while (NULL != (cmc = vl->cmc_tail)) { - GNUNET_break (0); - return GNUNET_SYSERR; + GNUNET_CONTAINER_DLL_remove (vl->cmc_head, vl->cmc_tail, cmc); + finish_cmc_handling (cmc); } - tc->type = CT_COMMUNICATOR; - size = ntohs (cam->header.size) - sizeof (*cam); - if (0 == size) - return GNUNET_OK; /* receive-only communicator */ - GNUNET_MQ_check_zero_termination (cam); - return GNUNET_OK; } @@ -3092,13 +4056,20 @@ handle_communicator_available ( struct TransportClient *tc = cls; uint16_t size; - size = ntohs (cam->header.size) - sizeof (*cam); + size = ntohs (cam->header.size) - sizeof(*cam); if (0 == size) - return; /* receive-only communicator */ + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receive-only communicator connected\n"); + return; /* receive-only communicator */ + } tc->details.communicator.address_prefix = GNUNET_strdup ((const char *) &cam[1]); tc->details.communicator.cc = (enum GNUNET_TRANSPORT_CommunicatorCharacteristics) ntohl (cam->cc); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Communicator with prefix `%s' connected\n", + tc->details.communicator.address_prefix); GNUNET_SERVICE_client_continue (tc->client); } @@ -3121,14 +4092,7 @@ check_communicator_backchannel ( uint16_t isize; (void) cls; - msize = ntohs (cb->header.size) - sizeof (*cb); - if (UINT16_MAX - msize > - sizeof (struct TransportBackchannelEncapsulationMessage) + - sizeof (struct TransportBackchannelRequestPayload)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } + msize = ntohs (cb->header.size) - sizeof(*cb); inbox = (const struct GNUNET_MessageHeader *) &cb[1]; isize = ntohs (inbox->size); if (isize >= msize) @@ -3139,7 +4103,7 @@ check_communicator_backchannel ( is = (const char *) inbox; is += isize; msize -= isize; - GNUNET_assert (msize > 0); + GNUNET_assert (0 < msize); if ('\0' != is[msize - 1]) { GNUNET_break (0); @@ -3150,102 +4114,37 @@ check_communicator_backchannel ( /** - * Remove memory used by expired ephemeral keys. + * Ensure ephemeral keys in our @a dv are current. If no current one exists, + * set it up. * - * @param cls NULL + * @param dv[in,out] virtual link to update ephemeral for */ static void -expire_ephemerals (void *cls) +update_ephemeral (struct DistanceVector *dv) { - struct EphemeralCacheEntry *ece; + struct EphemeralConfirmationPS ec; - (void) cls; - ephemeral_task = NULL; - while (NULL != (ece = GNUNET_CONTAINER_heap_peek (ephemeral_heap))) - { - if (0 == GNUNET_TIME_absolute_get_remaining (ece->ephemeral_validity) - .rel_value_us) - { - free_ephemeral (ece); - continue; - } - ephemeral_task = GNUNET_SCHEDULER_add_at (ece->ephemeral_validity, - &expire_ephemerals, - NULL); + if (0 != + GNUNET_TIME_absolute_get_remaining (dv->ephemeral_validity).rel_value_us) return; - } -} - - -/** - * Lookup ephemeral key in our #ephemeral_map. If no valid one exists, generate - * one, cache it and return it. - * - * @param pid peer to look up ephemeral for - * @param private_key[out] set to the private key - * @param ephemeral_key[out] set to the key - * @param ephemeral_sender_sig[out] set to the signature - * @param ephemeral_validity[out] set to the validity expiration time - */ -static void -lookup_ephemeral (const struct GNUNET_PeerIdentity *pid, - struct GNUNET_CRYPTO_EcdhePrivateKey *private_key, - struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral_key, - struct GNUNET_CRYPTO_EddsaSignature *ephemeral_sender_sig, - struct GNUNET_TIME_Absolute *ephemeral_validity) -{ - struct EphemeralCacheEntry *ece; - struct EphemeralConfirmation ec; - - ece = GNUNET_CONTAINER_multipeermap_get (ephemeral_map, pid); - if ((NULL != ece) && - (0 == GNUNET_TIME_absolute_get_remaining (ece->ephemeral_validity) - .rel_value_us)) - { - free_ephemeral (ece); - ece = NULL; - } - if (NULL == ece) - { - ece = GNUNET_new (struct EphemeralCacheEntry); - ece->target = *pid; - ece->ephemeral_validity = - GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get_monotonic (GST_cfg), - EPHEMERAL_VALIDITY); - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_ecdhe_key_create2 (&ece->private_key)); - GNUNET_CRYPTO_ecdhe_key_get_public (&ece->private_key, &ece->ephemeral_key); - ec.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL); - ec.purpose.size = htonl (sizeof (ec)); - ec.target = *pid; - ec.ephemeral_key = ece->ephemeral_key; - GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, - &ec.purpose, - &ece->sender_sig)); - ece->hn = - GNUNET_CONTAINER_heap_insert (ephemeral_heap, - ece, - ece->ephemeral_validity.abs_value_us); - GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multipeermap_put ( - ephemeral_map, - &ece->target, - ece, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - if (NULL == ephemeral_task) - ephemeral_task = GNUNET_SCHEDULER_add_at (ece->ephemeral_validity, - &expire_ephemerals, - NULL); - } - *private_key = ece->private_key; - *ephemeral_key = ece->ephemeral_key; - *ephemeral_sender_sig = ece->sender_sig; - *ephemeral_validity = ece->ephemeral_validity; + dv->monotime = GNUNET_TIME_absolute_get_monotonic (GST_cfg); + dv->ephemeral_validity = + GNUNET_TIME_absolute_add (dv->monotime, EPHEMERAL_VALIDITY); + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_ecdhe_key_create2 (&dv->private_key)); + GNUNET_CRYPTO_ecdhe_key_get_public (&dv->private_key, &dv->ephemeral_key); + ec.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL); + ec.purpose.size = htonl (sizeof(ec)); + ec.target = dv->target; + ec.ephemeral_key = dv->ephemeral_key; + GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, + &ec, + &dv->sender_sig); } /** - * Send the control message @a payload on @a queue. + * Send the message @a payload on @a queue. * * @param queue the queue to use for transmission * @param pm pending message to update once transmission is done, may be NULL! @@ -3263,6 +4162,14 @@ queue_send_msg (struct Queue *queue, struct GNUNET_TRANSPORT_SendMessageTo *smt; struct GNUNET_MQ_Envelope *env; + queue->idle = GNUNET_NO; + GNUNET_log ( + GNUNET_ERROR_TYPE_DEBUG, + "Queueing %u bytes of payload for transmission <%llu> on queue %llu to %s\n", + (unsigned int) payload_size, + (NULL == pm) ? 0 : pm->logging_uuid, + (unsigned long long) queue->qid, + GNUNET_i2s (&queue->neighbour->pid)); env = GNUNET_MQ_msg_extra (smt, payload_size, GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG); @@ -3281,50 +4188,20 @@ queue_send_msg (struct Queue *queue, { qe->pm = pm; GNUNET_assert (NULL == pm->qe); - pm->qe = qe; - } - GNUNET_CONTAINER_DLL_insert (queue->queue_head, queue->queue_tail, qe); - GNUNET_assert (CT_COMMUNICATOR == queue->tc->type); - queue->queue_length++; - queue->tc->details.communicator.total_queue_length++; - GNUNET_MQ_send (queue->tc->mq, env); - } -} - - -/** - * Which transmission options are allowable for transmission? - * Interpreted bit-wise! - */ -enum RouteMessageOptions -{ - /** - * Only confirmed, non-DV direct neighbours. - */ - RMO_NONE = 0, - - /** - * We are allowed to use DV routing for this @a hdr - */ - RMO_DV_ALLOWED = 1, - - /** - * We are allowed to use unconfirmed queues or DV routes for this message - */ - RMO_UNCONFIRMED_ALLOWED = 2, - - /** - * Reliable and unreliable, DV and non-DV are all acceptable. - */ - RMO_ANYTHING_GOES = (RMO_DV_ALLOWED | RMO_UNCONFIRMED_ALLOWED), - - /** - * If we have multiple choices, it is OK to send this message - * over multiple channels at the same time to improve loss tolerance. - * (We do at most 2 transmissions.) - */ - RMO_REDUNDANT = 4 -}; + pm->qe = qe; + } + GNUNET_CONTAINER_DLL_insert (queue->queue_head, queue->queue_tail, qe); + GNUNET_assert (CT_COMMUNICATOR == queue->tc->type); + queue->queue_length++; + queue->tc->details.communicator.total_queue_length++; + if (COMMUNICATOR_TOTAL_QUEUE_LIMIT == + queue->tc->details.communicator.total_queue_length) + queue->idle = GNUNET_NO; + if (QUEUE_LENGTH_LIMIT == queue->queue_length) + queue->idle = GNUNET_NO; + GNUNET_MQ_send (queue->tc->mq, env); + } +} /** @@ -3335,8 +4212,9 @@ enum RouteMessageOptions * @param hdr message to send as payload * @param options whether queues must be confirmed or not, * and whether we may pick multiple (2) queues + * @return expected RTT for transmission, #GNUNET_TIME_UNIT_FOREVER_REL if sending failed */ -static void +static struct GNUNET_TIME_Relative route_via_neighbour (const struct Neighbour *n, const struct GNUNET_MessageHeader *hdr, enum RouteMessageOptions options) @@ -3345,6 +4223,7 @@ route_via_neighbour (const struct Neighbour *n, unsigned int candidates; unsigned int sel1; unsigned int sel2; + struct GNUNET_TIME_Relative rtt; /* Pick one or two 'random' queues from n (under constraints of options) */ now = GNUNET_TIME_absolute_get (); @@ -3356,22 +4235,26 @@ route_via_neighbour (const struct Neighbour *n, for (struct Queue *pos = n->queue_head; NULL != pos; pos = pos->next_neighbour) { - /* Count the queue with the visibility task in all cases, as - otherwise we may end up with no queues just because the - time for the visibility task just expired but the scheduler - just ran this task first */ if ((0 == (options & RMO_UNCONFIRMED_ALLOWED)) || - (pos->validated_until.abs_value_us > now.abs_value_us) || - (NULL != pos->visibility_task)) + (pos->validated_until.abs_value_us > now.abs_value_us)) candidates++; } if (0 == candidates) { - /* Given that we above check for pos->visibility task, - this should be strictly impossible. */ - GNUNET_break (0); - return; + /* This can happen rarely if the last confirmed queue timed + out just as we were beginning to process this message. */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Could not route message of type %u to %s: no valid queue\n", + ntohs (hdr->type), + GNUNET_i2s (&n->pid)); + GNUNET_STATISTICS_update (GST_stats, + "# route selection failed (all no valid queue)", + 1, + GNUNET_NO); + return GNUNET_TIME_UNIT_FOREVER_REL; } + + rtt = GNUNET_TIME_UNIT_FOREVER_REL; sel1 = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, candidates); if (0 == (options & RMO_REDUNDANT)) sel2 = candidates; /* picks none! */ @@ -3381,183 +4264,31 @@ route_via_neighbour (const struct Neighbour *n, for (struct Queue *pos = n->queue_head; NULL != pos; pos = pos->next_neighbour) { - /* Count the queue with the visibility task in all cases, as - otherwise we may end up with no queues just because the - time for the visibility task just expired but the scheduler - just ran this task first */ - if ((pos->validated_until.abs_value_us > now.abs_value_us) || - (NULL != pos->visibility_task)) + if ((0 == (options & RMO_UNCONFIRMED_ALLOWED)) || + (pos->validated_until.abs_value_us > now.abs_value_us)) { if ((sel1 == candidates) || (sel2 == candidates)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Routing message of type %u to %s using %s (#%u)\n", + ntohs (hdr->type), + GNUNET_i2s (&n->pid), + pos->address, + (sel1 == candidates) ? 1 : 2); + rtt = GNUNET_TIME_relative_min (rtt, pos->pd.aged_rtt); queue_send_msg (pos, NULL, hdr, ntohs (hdr->size)); + } candidates++; } } -} - - -/** - * Given a distance vector path @a dvh route @a payload to - * the ultimate destination respecting @a options. - * Sets up the boxed message and queues it at the next hop. - * - * @param dvh choice of the path for the message - * @param payload body to transmit - * @param options options to use for control - */ -static void -forward_via_dvh (const struct DistanceVectorHop *dvh, - const struct GNUNET_MessageHeader *payload, - enum RouteMessageOptions options) -{ - uint16_t mlen = ntohs (payload->size); - char boxram[sizeof (struct TransportDVBox) + - (dvh->distance + 1) * sizeof (struct GNUNET_PeerIdentity) + - mlen] GNUNET_ALIGN; - struct TransportDVBox *box = (struct TransportDVBox *) boxram; - struct GNUNET_PeerIdentity *path = (struct GNUNET_PeerIdentity *) &box[1]; - - box->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX); - box->header.size = htons (sizeof (boxram)); - box->total_hops = htons (0); - box->num_hops = htons (dvh->distance + 1); - box->origin = GST_my_identity; - memcpy (path, dvh->path, dvh->distance * sizeof (struct GNUNET_PeerIdentity)); - path[dvh->distance] = dvh->dv->target; - memcpy (&path[dvh->distance + 1], payload, mlen); - route_via_neighbour (dvh->next_hop, &box->header, options); -} - - -/** - * Pick a path of @a dv under constraints @a options and schedule - * transmission of @a hdr. - * - * @param n neighbour to send to - * @param hdr message to send as payload - * @param options whether path must be confirmed or not - * and whether we may pick multiple (2) paths - */ -static void -route_via_dv (const struct DistanceVector *dv, - const struct GNUNET_MessageHeader *hdr, - enum RouteMessageOptions options) -{ - struct DistanceVectorHop *h1; - struct DistanceVectorHop *h2; - uint64_t num_dv; - uint64_t choice1; - uint64_t choice2; - - /* Pick random vectors, but weighted by distance, giving more weight - to shorter vectors */ - num_dv = 0; - for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; - pos = pos->next_dv) - { - if ((0 == (options & RMO_UNCONFIRMED_ALLOWED)) && - (GNUNET_TIME_absolute_get_remaining (pos->path_valid_until) - .rel_value_us == 0)) - continue; /* pos unconfirmed and confirmed required */ - num_dv += MAX_DV_HOPS_ALLOWED - pos->distance; - } - if (0 == num_dv) - { - GNUNET_break (0); - return; - } - choice1 = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, num_dv); - choice2 = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, num_dv); - num_dv = 0; - h1 = NULL; - h2 = NULL; - for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; - pos = pos->next_dv) - { - uint32_t delta = MAX_DV_HOPS_ALLOWED - pos->distance; - - if ((0 == (options & RMO_UNCONFIRMED_ALLOWED)) && - (GNUNET_TIME_absolute_get_remaining (pos->path_valid_until) - .rel_value_us == 0)) - continue; /* pos unconfirmed and confirmed required */ - if ((num_dv <= choice1) && (num_dv + delta > choice1)) - h1 = pos; - if ((num_dv <= choice2) && (num_dv + delta > choice2)) - h2 = pos; - num_dv += delta; - } - forward_via_dvh (h1, hdr, options & (~RMO_REDUNDANT)); - if (0 == (options & RMO_REDUNDANT)) - forward_via_dvh (h2, hdr, options & (~RMO_REDUNDANT)); -} - - -/** - * We need to transmit @a hdr to @a target. If necessary, this may - * involve DV routing. - * - * @param target peer to receive @a hdr - * @param hdr header of the message to route and #GNUNET_free() - * @param options which transmission channels are allowed - */ -static void -route_message (const struct GNUNET_PeerIdentity *target, - struct GNUNET_MessageHeader *hdr, - enum RouteMessageOptions options) -{ - struct Neighbour *n; - struct DistanceVector *dv; - - n = GNUNET_CONTAINER_multipeermap_get (neighbours, target); - dv = (0 != (options & RMO_DV_ALLOWED)) - ? GNUNET_CONTAINER_multipeermap_get (dv_routes, target) - : NULL; - if (0 == (options & RMO_UNCONFIRMED_ALLOWED)) - { - /* if confirmed is required, and we do not have anything - confirmed, drop respective options */ - if ((NULL != n) && (GNUNET_NO == n->core_visible)) - n = NULL; - if ((NULL != dv) && (GNUNET_NO == dv->core_visible)) - dv = NULL; - } - if ((NULL == n) && (NULL == dv)) - { - GNUNET_STATISTICS_update (GST_stats, - "# Messages dropped in routing: no acceptable method", - 1, - GNUNET_NO); - GNUNET_free (hdr); - return; - } - /* If both dv and n are possible and we must choose: - flip a coin for the choice between the two; for now 50/50 */ - if ((NULL != n) && (NULL != dv) && (0 == (options & RMO_REDUNDANT))) - { - if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 2)) - n = NULL; - else - dv = NULL; - } - if ((NULL != n) && (NULL != dv)) - options &= ~RMO_REDUNDANT; /* We will do one DV and one direct, that's - enough for redunancy, so clear the flag. */ - if (NULL != n) - { - route_via_neighbour (n, hdr, options); - } - if (NULL != dv) - { - route_via_dv (dv, hdr, options); - } - GNUNET_free (hdr); + return rtt; } /** * Structure of the key material used to encrypt backchannel messages. */ -struct BackchannelKeyState +struct DVKeyState { /** * State of our block cipher. @@ -3569,7 +4300,6 @@ struct BackchannelKeyState */ struct { - /** * Key used for HMAC calculations (via #GNUNET_CRYPTO_hmac()). */ @@ -3584,36 +4314,47 @@ struct BackchannelKeyState * Counter value to use during setup. */ char aes_ctr[128 / 8]; - } material; }; +/** + * Given the key material in @a km and the initialization vector + * @a iv, setup the key material for the backchannel in @a key. + * + * @param km raw master secret + * @param iv initialization vector + * @param key[out] symmetric cipher and HMAC state to generate + */ static void -bc_setup_key_state_from_km (const struct GNUNET_HashCode *km, +dv_setup_key_state_from_km (const struct GNUNET_HashCode *km, const struct GNUNET_ShortHashCode *iv, - struct BackchannelKeyState *key) + struct DVKeyState *key) { /* must match #dh_key_derive_eph_pub */ GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_kdf (&key->material, - sizeof (key->material), + sizeof(key->material), "transport-backchannel-key", strlen ("transport-backchannel-key"), &km, - sizeof (km), + sizeof(km), iv, - sizeof (*iv))); + sizeof(*iv))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Deriving backchannel key based on KM %s and IV %s\n", + GNUNET_h2s (km), + GNUNET_sh2s (iv)); gcry_cipher_open (&key->cipher, GCRY_CIPHER_AES256 /* low level: go for speed */, GCRY_CIPHER_MODE_CTR, 0 /* flags */); gcry_cipher_setkey (key->cipher, &key->material.aes_key, - sizeof (key->material.aes_key)); + sizeof(key->material.aes_key)); gcry_cipher_setctr (key->cipher, &key->material.aes_ctr, - sizeof (key->material.aes_ctr)); + sizeof(key->material.aes_ctr)); } @@ -3631,14 +4372,14 @@ dh_key_derive_eph_pid ( const struct GNUNET_CRYPTO_EcdhePrivateKey *priv_ephemeral, const struct GNUNET_PeerIdentity *target, const struct GNUNET_ShortHashCode *iv, - struct BackchannelKeyState *key) + struct DVKeyState *key) { struct GNUNET_HashCode km; GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_ecdh_eddsa (priv_ephemeral, &target->public_key, &km)); - bc_setup_key_state_from_km (&km, iv, key); + dv_setup_key_state_from_km (&km, iv, key); } @@ -3654,91 +4395,530 @@ dh_key_derive_eph_pid ( static void dh_key_derive_eph_pub (const struct GNUNET_CRYPTO_EcdhePublicKey *pub_ephemeral, const struct GNUNET_ShortHashCode *iv, - struct BackchannelKeyState *key) + struct DVKeyState *key) { struct GNUNET_HashCode km; - GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_eddsa_ecdh (GST_my_private_key, - pub_ephemeral, - &km)); - bc_setup_key_state_from_km (&km, iv, key); + GNUNET_assert (GNUNET_YES == GNUNET_CRYPTO_eddsa_ecdh (GST_my_private_key, + pub_ephemeral, + &km)); + dv_setup_key_state_from_km (&km, iv, key); +} + + +/** + * Do HMAC calculation for backchannel messages over @a data using key + * material from @a key. + * + * @param key key material (from DH) + * @param hmac[out] set to the HMAC + * @param data data to perform HMAC calculation over + * @param data_size number of bytes in @a data + */ +static void +dv_hmac (const struct DVKeyState *key, + struct GNUNET_HashCode *hmac, + const void *data, + size_t data_size) +{ + GNUNET_CRYPTO_hmac (&key->material.hmac_key, data, data_size, hmac); +} + + +/** + * Perform backchannel encryption using symmetric secret in @a key + * to encrypt data from @a in to @a dst. + * + * @param key[in,out] key material to use + * @param dst where to write the result + * @param in input data to encrypt (plaintext) + * @param in_size number of bytes of input in @a in and available at @a dst + */ +static void +dv_encrypt (struct DVKeyState *key, const void *in, void *dst, size_t in_size) +{ + GNUNET_assert (0 == + gcry_cipher_encrypt (key->cipher, dst, in_size, in, in_size)); +} + + +/** + * Perform backchannel encryption using symmetric secret in @a key + * to encrypt data from @a in to @a dst. + * + * @param key[in,out] key material to use + * @param ciph cipher text to decrypt + * @param out[out] output data to generate (plaintext) + * @param out_size number of bytes of input in @a ciph and available in @a out + */ +static void +dv_decrypt (struct DVKeyState *key, + void *out, + const void *ciph, + size_t out_size) +{ + GNUNET_assert ( + 0 == gcry_cipher_decrypt (key->cipher, out, out_size, ciph, out_size)); +} + + +/** + * Clean up key material in @a key. + * + * @param key key material to clean up (memory must not be free'd!) + */ +static void +dv_key_clean (struct DVKeyState *key) +{ + gcry_cipher_close (key->cipher); + GNUNET_CRYPTO_zero_keys (&key->material, sizeof(key->material)); +} + + +/** + * Function to call to further operate on the now DV encapsulated + * message @a hdr, forwarding it via @a next_hop under respect of + * @a options. + * + * @param cls closure + * @param next_hop next hop of the DV path + * @param hdr encapsulated message, technically a `struct TransportDFBoxMessage` + * @param options options of the original message + */ +typedef void (*DVMessageHandler) (void *cls, + struct Neighbour *next_hop, + const struct GNUNET_MessageHeader *hdr, + enum RouteMessageOptions options); + +/** + * Pick a path of @a dv under constraints @a options and schedule + * transmission of @a hdr. + * + * @param target neighbour to ultimately send to + * @param num_dvhs length of the @a dvhs array + * @param dvhs array of hops to send the message to + * @param hdr message to send as payload + * @param use function to call with the encapsulated message + * @param use_cls closure for @a use + * @param options whether path must be confirmed or not, to be passed to @a use + * @return expected RTT for transmission, #GNUNET_TIME_UNIT_FOREVER_REL if sending failed + */ +static struct GNUNET_TIME_Relative +encapsulate_for_dv (struct DistanceVector *dv, + unsigned int num_dvhs, + struct DistanceVectorHop **dvhs, + const struct GNUNET_MessageHeader *hdr, + DVMessageHandler use, + void *use_cls, + enum RouteMessageOptions options) +{ + struct TransportDVBoxMessage box_hdr; + struct TransportDVBoxPayloadP payload_hdr; + uint16_t enc_body_size = ntohs (hdr->size); + char enc[sizeof(struct TransportDVBoxPayloadP) + enc_body_size] GNUNET_ALIGN; + struct TransportDVBoxPayloadP *enc_payload_hdr = + (struct TransportDVBoxPayloadP *) enc; + struct DVKeyState key; + struct GNUNET_TIME_Relative rtt; + + /* Encrypt payload */ + box_hdr.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX); + box_hdr.total_hops = htons (0); + update_ephemeral (dv); + box_hdr.ephemeral_key = dv->ephemeral_key; + payload_hdr.sender_sig = dv->sender_sig; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &box_hdr.iv, + sizeof(box_hdr.iv)); + dh_key_derive_eph_pid (&dv->private_key, &dv->target, &box_hdr.iv, &key); + payload_hdr.sender = GST_my_identity; + payload_hdr.monotonic_time = GNUNET_TIME_absolute_hton (dv->monotime); + dv_encrypt (&key, &payload_hdr, enc_payload_hdr, sizeof(payload_hdr)); + dv_encrypt (&key, + hdr, + &enc[sizeof(struct TransportDVBoxPayloadP)], + enc_body_size); + dv_hmac (&key, &box_hdr.hmac, enc, sizeof(enc)); + dv_key_clean (&key); + rtt = GNUNET_TIME_UNIT_FOREVER_REL; + /* For each selected path, take the pre-computed header and body + and add the path in the middle of the message; then send it. */ + for (unsigned int i = 0; i < num_dvhs; i++) + { + struct DistanceVectorHop *dvh = dvhs[i]; + unsigned int num_hops = dvh->distance + 1; + char buf[sizeof(struct TransportDVBoxMessage) + + sizeof(struct GNUNET_PeerIdentity) * num_hops + + sizeof(struct TransportDVBoxPayloadP) + + enc_body_size] GNUNET_ALIGN; + struct GNUNET_PeerIdentity *dhops; + + box_hdr.header.size = htons (sizeof(buf)); + box_hdr.num_hops = htons (num_hops); + memcpy (buf, &box_hdr, sizeof(box_hdr)); + dhops = (struct GNUNET_PeerIdentity *) &buf[sizeof(box_hdr)]; + memcpy (dhops, + dvh->path, + dvh->distance * sizeof(struct GNUNET_PeerIdentity)); + dhops[dvh->distance] = dv->target; + if (GNUNET_EXTRA_LOGGING > 0) + { + char *path; + + path = GNUNET_strdup (GNUNET_i2s (&GST_my_identity)); + for (unsigned int j = 0; j <= num_hops; j++) + { + char *tmp; + + GNUNET_asprintf (&tmp, "%s-%s", path, GNUNET_i2s (&dhops[j])); + GNUNET_free (path); + path = tmp; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Routing message of type %u to %s using DV (#%u/%u) via %s\n", + ntohs (hdr->type), + GNUNET_i2s (&dv->target), + i + 1, + num_dvhs + 1, + path); + GNUNET_free (path); + } + rtt = GNUNET_TIME_relative_min (rtt, dvh->pd.aged_rtt); + memcpy (&dhops[num_hops], enc, sizeof(enc)); + use (use_cls, + dvh->next_hop, + (const struct GNUNET_MessageHeader *) buf, + options); + } + return rtt; +} + + +/** + * Wrapper around #route_via_neighbour() that matches the + * #DVMessageHandler structure. + * + * @param cls unused + * @param next_hop where to send next + * @param hdr header of the message to send + * @param options message options for queue selection + */ +static void +send_dv_to_neighbour (void *cls, + struct Neighbour *next_hop, + const struct GNUNET_MessageHeader *hdr, + enum RouteMessageOptions options) +{ + (void) cls; + (void) route_via_neighbour (next_hop, hdr, options); +} + + +/** + * We need to transmit @a hdr to @a target. If necessary, this may + * involve DV routing. This function routes without applying flow + * control or congestion control and should only be used for control + * traffic. + * + * @param target peer to receive @a hdr + * @param hdr header of the message to route and #GNUNET_free() + * @param options which transmission channels are allowed + * @return expected RTT for transmission, #GNUNET_TIME_UNIT_FOREVER_REL if sending failed + */ +static struct GNUNET_TIME_Relative +route_control_message_without_fc (const struct GNUNET_PeerIdentity *target, + const struct GNUNET_MessageHeader *hdr, + enum RouteMessageOptions options) +{ + struct VirtualLink *vl; + struct Neighbour *n; + struct DistanceVector *dv; + struct GNUNET_TIME_Relative rtt1; + struct GNUNET_TIME_Relative rtt2; + + vl = lookup_virtual_link (target); + GNUNET_assert (NULL != vl); + n = vl->n; + dv = (0 != (options & RMO_DV_ALLOWED)) ? vl->dv : NULL; + if (0 == (options & RMO_UNCONFIRMED_ALLOWED)) + { + /* if confirmed is required, and we do not have anything + confirmed, drop respective options */ + if (NULL == n) + n = lookup_neighbour (target); + if ((NULL == dv) && (0 != (options & RMO_DV_ALLOWED))) + dv = GNUNET_CONTAINER_multipeermap_get (dv_routes, target); + } + if ((NULL == n) && (NULL == dv)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Cannot route message of type %u to %s: no route\n", + ntohs (hdr->type), + GNUNET_i2s (target)); + GNUNET_STATISTICS_update (GST_stats, + "# Messages dropped in routing: no acceptable method", + 1, + GNUNET_NO); + return GNUNET_TIME_UNIT_FOREVER_REL; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Routing message of type %u to %s with options %X\n", + ntohs (hdr->type), + GNUNET_i2s (target), + (unsigned int) options); + /* If both dv and n are possible and we must choose: + flip a coin for the choice between the two; for now 50/50 */ + if ((NULL != n) && (NULL != dv) && (0 == (options & RMO_REDUNDANT))) + { + if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 2)) + n = NULL; + else + dv = NULL; + } + if ((NULL != n) && (NULL != dv)) + options &= ~RMO_REDUNDANT; /* We will do one DV and one direct, that's + enough for redunancy, so clear the flag. */ + rtt1 = GNUNET_TIME_UNIT_FOREVER_REL; + rtt2 = GNUNET_TIME_UNIT_FOREVER_REL; + if (NULL != n) + { + rtt1 = route_via_neighbour (n, hdr, options); + } + if (NULL != dv) + { + struct DistanceVectorHop *hops[2]; + unsigned int res; + + res = pick_random_dv_hops (dv, + options, + hops, + (0 == (options & RMO_REDUNDANT)) ? 1 : 2); + if (0 == res) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Failed to route message, could not determine DV path\n"); + return rtt1; + } + rtt2 = encapsulate_for_dv (dv, + res, + hops, + hdr, + &send_dv_to_neighbour, + NULL, + options & (~RMO_REDUNDANT)); + } + return GNUNET_TIME_relative_min (rtt1, rtt2); } /** - * Do HMAC calculation for backchannel messages over @a data using key - * material from @a key. + * Something changed on the virtual link with respect to flow + * control. Consider retransmitting the FC window size. * - * @param key key material (from DH) - * @param hmac[out] set to the HMAC - * @param data data to perform HMAC calculation over - * @param data_size number of bytes in @a data + * @param cls a `struct VirtualLink` to work with */ static void -bc_hmac (const struct BackchannelKeyState *key, - struct GNUNET_HashCode *hmac, - const void *data, - size_t data_size) +consider_sending_fc (void *cls) { - GNUNET_CRYPTO_hmac (&key->material.hmac_key, data, data_size, hmac); -} + struct VirtualLink *vl = cls; + struct GNUNET_TIME_Absolute monotime; + struct TransportFlowControlMessage fc; + struct GNUNET_TIME_Relative duration; + struct GNUNET_TIME_Relative rtt; + duration = GNUNET_TIME_absolute_get_duration (vl->last_fc_transmission); + /* OPTIMIZE-FC-BDP: decide sane criteria on when to do this, instead of doing + it always! */ + /* For example, we should probably ONLY do this if a bit more than + an RTT has passed, or if the window changed "significantly" since + then. See vl->last_fc_rtt! NOTE: to do this properly, we also + need an estimate for the bandwidth-delay-product for the entire + VL, as that determines "significantly". We have the delay, but + the bandwidth statistics need to be added for the VL!*/(void) duration; -/** - * Perform backchannel encryption using symmetric secret in @a key - * to encrypt data from @a in to @a dst. - * - * @param key[in,out] key material to use - * @param dst where to write the result - * @param in input data to encrypt (plaintext) - * @param in_size number of bytes of input in @a in and available at @a dst - */ -static void -bc_encrypt (struct BackchannelKeyState *key, - const void *in, - void *dst, - size_t in_size) -{ - GNUNET_assert (0 == - gcry_cipher_encrypt (key->cipher, dst, in_size, in, in_size)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending FC seq %u to %s with new window %llu\n", + (unsigned int) vl->fc_seq_gen, + GNUNET_i2s (&vl->target), + (unsigned long long) vl->incoming_fc_window_size); + monotime = GNUNET_TIME_absolute_get_monotonic (GST_cfg); + vl->last_fc_transmission = monotime; + fc.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL); + fc.header.size = htons (sizeof(fc)); + fc.seq = htonl (vl->fc_seq_gen++); + fc.inbound_window_size = GNUNET_htonll (vl->incoming_fc_window_size); + fc.outbound_sent = GNUNET_htonll (vl->outbound_fc_window_size_used); + fc.outbound_window_size = GNUNET_htonll (vl->outbound_fc_window_size); + fc.sender_time = GNUNET_TIME_absolute_hton (monotime); + rtt = route_control_message_without_fc (&vl->target, &fc.header, RMO_NONE); + if (GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us == rtt.rel_value_us) + { + rtt = GNUNET_TIME_UNIT_SECONDS; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "FC retransmission to %s failed, will retry in %s\n", + GNUNET_i2s (&vl->target), + GNUNET_STRINGS_relative_time_to_string (rtt, GNUNET_YES)); + vl->last_fc_rtt = GNUNET_TIME_UNIT_ZERO; + } + else + { + /* OPTIMIZE-FC-BDP: rtt is not ideal, we can do better! */ + vl->last_fc_rtt = rtt; + } + if (NULL != vl->fc_retransmit_task) + GNUNET_SCHEDULER_cancel (vl->fc_retransmit_task); + vl->fc_retransmit_task = + GNUNET_SCHEDULER_add_delayed (rtt, &consider_sending_fc, vl); } /** - * Perform backchannel encryption using symmetric secret in @a key - * to encrypt data from @a in to @a dst. + * There is a message at the head of the pending messages for @a vl + * which may be ready for transmission. Check if a queue is ready to + * take it. * - * @param key[in,out] key material to use - * @param ciph cipher text to decrypt - * @param out[out] output data to generate (plaintext) - * @param out_size number of bytes of input in @a ciph and available in @a out + * This function must (1) check for flow control to ensure that we can + * right now send to @a vl, (2) check that the pending message in the + * queue is actually eligible, (3) determine if any applicable queue + * (direct neighbour or DVH path) is ready to accept messages, and + * (4) prioritize based on the preferences associated with the + * pending message. + * + * So yeah, easy. + * + * @param vl virtual link where we should check for transmission */ static void -bc_decrypt (struct BackchannelKeyState *key, - void *out, - const void *ciph, - size_t out_size) +check_vl_transmission (struct VirtualLink *vl) { - GNUNET_assert ( - 0 == gcry_cipher_decrypt (key->cipher, out, out_size, ciph, out_size)); + struct Neighbour *n = vl->n; + struct DistanceVector *dv = vl->dv; + struct GNUNET_TIME_Absolute now; + int elig; + + /* Check that we have an eligible pending message! + (cheaper than having #transmit_on_queue() find out!) */ + elig = GNUNET_NO; + for (struct PendingMessage *pm = vl->pending_msg_head; NULL != pm; + pm = pm->next_vl) + { + if (NULL != pm->qe) + continue; /* not eligible, is in a queue! */ + if (pm->bytes_msg + vl->outbound_fc_window_size_used > + vl->outbound_fc_window_size) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Stalled transmision on VL %s due to flow control: %llu < %llu\n", + GNUNET_i2s (&vl->target), + (unsigned long long) vl->outbound_fc_window_size, + (unsigned long long) (pm->bytes_msg + + vl->outbound_fc_window_size_used)); + consider_sending_fc (vl); + return; /* We have a message, but flow control says "nope" */ + } + elig = GNUNET_YES; + break; + } + if (GNUNET_NO == elig) + return; + + /* Notify queues at direct neighbours that we are interested */ + now = GNUNET_TIME_absolute_get (); + if (NULL != n) + { + for (struct Queue *queue = n->queue_head; NULL != queue; + queue = queue->next_neighbour) + if ((GNUNET_YES == queue->idle) && + (queue->validated_until.abs_value_us > now.abs_value_us)) + schedule_transmit_on_queue (queue, GNUNET_SCHEDULER_PRIORITY_DEFAULT); + } + /* Notify queues via DV that we are interested */ + if (NULL != dv) + { + /* Do DV with lower scheduler priority, which effectively means that + IF a neighbour exists and is available, we prefer it. */ + for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; + pos = pos->next_dv) + { + struct Neighbour *nh = pos->next_hop; + + if (pos->path_valid_until.abs_value_us <= now.abs_value_us) + continue; /* skip this one: path not validated */ + for (struct Queue *queue = nh->queue_head; NULL != queue; + queue = queue->next_neighbour) + if ((GNUNET_YES == queue->idle) && + (queue->validated_until.abs_value_us > now.abs_value_us)) + schedule_transmit_on_queue (queue, + GNUNET_SCHEDULER_PRIORITY_BACKGROUND); + } + } } /** - * Clean up key material in @a key. + * Client asked for transmission to a peer. Process the request. * - * @param key key material to clean up (memory must not be free'd!) + * @param cls the client + * @param obm the send message that was sent */ static void -bc_key_clean (struct BackchannelKeyState *key) +handle_client_send (void *cls, const struct OutboundMessage *obm) { - gcry_cipher_close (key->cipher); - GNUNET_CRYPTO_zero_keys (&key->material, sizeof (key->material)); + struct TransportClient *tc = cls; + struct PendingMessage *pm; + const struct GNUNET_MessageHeader *obmm; + uint32_t bytes_msg; + struct VirtualLink *vl; + enum GNUNET_MQ_PriorityPreferences pp; + + GNUNET_assert (CT_CORE == tc->type); + obmm = (const struct GNUNET_MessageHeader *) &obm[1]; + bytes_msg = ntohs (obmm->size); + pp = (enum GNUNET_MQ_PriorityPreferences) ntohl (obm->priority); + vl = lookup_virtual_link (&obm->peer); + if (NULL == vl) + { + /* Failure: don't have this peer as a neighbour (anymore). + Might have gone down asynchronously, so this is NOT + a protocol violation by CORE. Still count the event, + as this should be rare. */ + GNUNET_SERVICE_client_continue (tc->client); + GNUNET_STATISTICS_update (GST_stats, + "# messages dropped (neighbour unknown)", + 1, + GNUNET_NO); + return; + } + + pm = GNUNET_malloc (sizeof(struct PendingMessage) + bytes_msg); + pm->logging_uuid = logging_uuid_gen++; + pm->prefs = pp; + pm->client = tc; + pm->vl = vl; + pm->bytes_msg = bytes_msg; + memcpy (&pm[1], obmm, bytes_msg); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending %u bytes as <%llu> to %s\n", + bytes_msg, + pm->logging_uuid, + GNUNET_i2s (&obm->peer)); + GNUNET_CONTAINER_MDLL_insert (client, + tc->details.core.pending_msg_head, + tc->details.core.pending_msg_tail, + pm); + GNUNET_CONTAINER_MDLL_insert (vl, + vl->pending_msg_head, + vl->pending_msg_tail, + pm); + check_vl_transmission (vl); } /** * Communicator requests backchannel transmission. Process the request. + * Just repacks it into our `struct TransportBackchannelEncapsulationMessage *` + * (which for now has exactly the same format, only a different message type) + * and passes it on for routing. * * @param cls the client * @param cb the send message that was sent @@ -3749,46 +4929,34 @@ handle_communicator_backchannel ( const struct GNUNET_TRANSPORT_CommunicatorBackchannel *cb) { struct TransportClient *tc = cls; - struct GNUNET_CRYPTO_EcdhePrivateKey private_key; - struct GNUNET_TIME_Absolute ephemeral_validity; - struct TransportBackchannelEncapsulationMessage *enc; - struct TransportBackchannelRequestPayload ppay; - struct BackchannelKeyState key; - char *mpos; - uint16_t msize; - + const struct GNUNET_MessageHeader *inbox = + (const struct GNUNET_MessageHeader *) &cb[1]; + uint16_t isize = ntohs (inbox->size); + const char *is = ((const char *) &cb[1]) + isize; + char + mbuf[isize + + sizeof(struct + TransportBackchannelEncapsulationMessage)] GNUNET_ALIGN; + struct TransportBackchannelEncapsulationMessage *be = + (struct TransportBackchannelEncapsulationMessage *) mbuf; + + /* 0-termination of 'is' was checked already in + #check_communicator_backchannel() */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Preparing backchannel transmission to %s:%s of type %u\n", + GNUNET_i2s (&cb->pid), + is, + ntohs (inbox->size)); /* encapsulate and encrypt message */ - msize = ntohs (cb->header.size) - sizeof (*cb) + - sizeof (struct TransportBackchannelRequestPayload); - enc = GNUNET_malloc (sizeof (*enc) + msize); - enc->header.type = + be->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_BACKCHANNEL_ENCAPSULATION); - enc->header.size = htons (sizeof (*enc) + msize); - enc->target = cb->pid; - lookup_ephemeral (&cb->pid, - &private_key, - &enc->ephemeral_key, - &ppay.sender_sig, - &ephemeral_validity); - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &enc->iv, - sizeof (enc->iv)); - dh_key_derive_eph_pid (&private_key, &cb->pid, &enc->iv, &key); - ppay.ephemeral_validity = GNUNET_TIME_absolute_hton (ephemeral_validity); - ppay.monotonic_time = - GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (GST_cfg)); - mpos = (char *) &enc[1]; - bc_encrypt (&key, &ppay, mpos, sizeof (ppay)); - bc_encrypt (&key, - &cb[1], - &mpos[sizeof (ppay)], - ntohs (cb->header.size) - sizeof (*cb)); - bc_hmac (&key, - &enc->hmac, - mpos, - sizeof (ppay) + ntohs (cb->header.size) - sizeof (*cb)); - bc_key_clean (&key); - route_message (&cb->pid, &enc->header, RMO_DV_ALLOWED); + be->header.size = htons (sizeof(mbuf)); + memcpy (&be[1], inbox, isize); + memcpy (&mbuf[sizeof(struct TransportBackchannelEncapsulationMessage) + + isize], + is, + strlen (is) + 1); + route_control_message_without_fc (&cb->pid, &be->header, RMO_DV_ALLOWED); GNUNET_SERVICE_client_continue (tc->client); } @@ -3841,6 +5009,10 @@ peerstore_store_own_cb (void *cls, int success) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to store our own address `%s' in peerstore!\n", ale->address); + else + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Successfully stored our own address `%s' in peerstore!\n", + ale->address); /* refresh period is 1/4 of expiration time, that should be plenty without being excessive. */ ale->st = @@ -3866,9 +5038,13 @@ store_pi (void *cls) ale->st = NULL; expiration = GNUNET_TIME_relative_to_absolute (ale->expiration); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Storing our address `%s' in peerstore until %s!\n", + ale->address, + GNUNET_STRINGS_absolute_time_to_string (expiration)); GNUNET_HELLO_sign_address (ale->address, ale->nt, - expiration, + hello_mono_time, GST_my_private_key, &addr, &addr_len); @@ -3908,8 +5084,12 @@ handle_add_address (void *cls, struct AddressListEntry *ale; size_t slen; - slen = ntohs (aam->header.size) - sizeof (*aam); - ale = GNUNET_malloc (sizeof (struct AddressListEntry) + slen); + /* 0-termination of &aam[1] was checked in #check_add_address */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Communicator added address `%s'!\n", + (const char *) &aam[1]); + slen = ntohs (aam->header.size) - sizeof(*aam); + ale = GNUNET_malloc (sizeof(struct AddressListEntry) + slen); ale->tc = tc; ale->address = (const char *) &ale[1]; ale->expiration = GNUNET_TIME_relative_ntoh (aam->expiration); @@ -3935,6 +5115,7 @@ handle_del_address (void *cls, const struct GNUNET_TRANSPORT_DelAddressMessage *dam) { struct TransportClient *tc = cls; + struct AddressListEntry *alen; if (CT_COMMUNICATOR != tc->type) { @@ -3944,11 +5125,15 @@ handle_del_address (void *cls, } for (struct AddressListEntry *ale = tc->details.communicator.addr_head; NULL != ale; - ale = ale->next) + ale = alen) { + alen = ale->next; if (dam->aid != ale->aid) continue; GNUNET_assert (ale->tc == tc); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Communicator deleted address `%s'!\n", + ale->address); free_address_list_entry (ale); GNUNET_SERVICE_client_continue (tc->client); } @@ -3957,30 +5142,6 @@ handle_del_address (void *cls, } -/** - * Context from #handle_incoming_msg(). Closure for many - * message handlers below. - */ -struct CommunicatorMessageContext -{ - /** - * Which communicator provided us with the message. - */ - struct TransportClient *tc; - - /** - * Additional information for flow control and about the sender. - */ - struct GNUNET_TRANSPORT_IncomingMessage im; - - /** - * Number of hops the message has travelled (if DV-routed). - * FIXME: make use of this in ACK handling! - */ - uint16_t total_hops; -}; - - /** * Given an inbound message @a msg from a communicator @a cmc, * demultiplex it based on the type calling the right handler. @@ -3994,27 +5155,30 @@ demultiplex_with_cmc (struct CommunicatorMessageContext *cmc, /** - * Send ACK to communicator (if requested) and free @a cmc. + * Function called when we are done giving a message of a certain + * size to CORE and should thus decrement the number of bytes of + * RAM reserved for that peer's MQ. * - * @param cmc context for which we are done handling the message + * @param cls a `struct CoreSentContext` */ static void -finish_cmc_handling (struct CommunicatorMessageContext *cmc) +core_env_sent_cb (void *cls) { - if (0 != ntohl (cmc->im.fc_on)) - { - /* send ACK when done to communicator for flow control! */ - struct GNUNET_MQ_Envelope *env; - struct GNUNET_TRANSPORT_IncomingMessageAck *ack; + struct CoreSentContext *ctx = cls; + struct VirtualLink *vl = ctx->vl; - env = GNUNET_MQ_msg (ack, GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG_ACK); - ack->reserved = htonl (0); - ack->fc_id = cmc->im.fc_id; - ack->sender = cmc->im.sender; - GNUNET_MQ_send (cmc->tc->mq, env); + if (NULL == vl) + { + /* lost the link in the meantime, ignore */ + GNUNET_free (ctx); + return; } - GNUNET_SERVICE_client_continue (cmc->tc->client); - GNUNET_free (cmc); + GNUNET_CONTAINER_DLL_remove (vl->csc_head, vl->csc_tail, ctx); + GNUNET_assert (vl->incoming_fc_window_size_ram >= ctx->size); + vl->incoming_fc_window_size_ram -= ctx->size; + vl->incoming_fc_window_size_used += ctx->isize; + consider_sending_fc (vl); + GNUNET_free (ctx); } @@ -4030,10 +5194,12 @@ static void handle_raw_message (void *cls, const struct GNUNET_MessageHeader *mh) { struct CommunicatorMessageContext *cmc = cls; + struct VirtualLink *vl; uint16_t size = ntohs (mh->size); + int have_core; - if ((size > UINT16_MAX - sizeof (struct InboundMessage)) || - (size < sizeof (struct GNUNET_MessageHeader))) + if ((size > UINT16_MAX - sizeof(struct InboundMessage)) || + (size < sizeof(struct GNUNET_MessageHeader))) { struct GNUNET_SERVICE_Client *client = cmc->tc->client; @@ -4042,23 +5208,91 @@ handle_raw_message (void *cls, const struct GNUNET_MessageHeader *mh) GNUNET_SERVICE_client_drop (client); return; } + vl = lookup_virtual_link (&cmc->im.sender); + if (NULL == vl) + { + /* FIXME: sender is giving us messages for CORE but we don't have + the link up yet! I *suspect* this can happen right now (i.e. + sender has verified us, but we didn't verify sender), but if + we pass this on, CORE would be confused (link down, messages + arrive). We should investigate more if this happens often, + or in a persistent manner, and possibly do "something" about + it. Thus logging as error for now. */GNUNET_break_op (0); + GNUNET_STATISTICS_update (GST_stats, + "# CORE messages droped (virtual link still down)", + 1, + GNUNET_NO); + + finish_cmc_handling (cmc); + return; + } + if (vl->incoming_fc_window_size_ram > UINT_MAX - size) + { + GNUNET_STATISTICS_update (GST_stats, + "# CORE messages droped (FC arithmetic overflow)", + 1, + GNUNET_NO); + + finish_cmc_handling (cmc); + return; + } + if (vl->incoming_fc_window_size_ram + size > vl->available_fc_window_size) + { + GNUNET_STATISTICS_update (GST_stats, + "# CORE messages droped (FC window overflow)", + 1, + GNUNET_NO); + finish_cmc_handling (cmc); + return; + } + /* Forward to all CORE clients */ + have_core = GNUNET_NO; for (struct TransportClient *tc = clients_head; NULL != tc; tc = tc->next) { struct GNUNET_MQ_Envelope *env; struct InboundMessage *im; + struct CoreSentContext *ctx; if (CT_CORE != tc->type) continue; + vl->incoming_fc_window_size_ram += size; env = GNUNET_MQ_msg_extra (im, size, GNUNET_MESSAGE_TYPE_TRANSPORT_RECV); + ctx = GNUNET_new (struct CoreSentContext); + ctx->vl = vl; + ctx->size = size; + ctx->isize = (GNUNET_NO == have_core) ? size : 0; + have_core = GNUNET_YES; + GNUNET_CONTAINER_DLL_insert (vl->csc_head, vl->csc_tail, ctx); + GNUNET_MQ_notify_sent (env, &core_env_sent_cb, ctx); im->peer = cmc->im.sender; memcpy (&im[1], mh, size); GNUNET_MQ_send (tc->mq, env); + vl->core_recv_window--; } - /* FIXME: consider doing this _only_ once the message - was drained from the CORE MQs to extend flow control to CORE! - (basically, increment counter in cmc, decrement on MQ send continuation! */ - finish_cmc_handling (cmc); + if (GNUNET_NO == have_core) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Dropped message to CORE: no CORE client connected!\n"); + /* Nevertheless, count window as used, as it is from the + perspective of the other peer! */ + vl->incoming_fc_window_size_used += size; + /* TODO-M1 */ + finish_cmc_handling (cmc); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Delivered message from %s of type %u to CORE\n", + GNUNET_i2s (&cmc->im.sender), + ntohs (mh->type)); + if (vl->core_recv_window > 0) + { + finish_cmc_handling (cmc); + return; + } + /* Wait with calling #finish_cmc_handling(cmc) until the message + was processed by CORE MQs (for CORE flow control)! */ + GNUNET_CONTAINER_DLL_insert (vl->cmc_head, vl->cmc_tail, cmc); } @@ -4070,11 +5304,12 @@ handle_raw_message (void *cls, const struct GNUNET_MessageHeader *mh) * @return #GNUNET_YES if message is well-formed */ static int -check_fragment_box (void *cls, const struct TransportFragmentBox *fb) +check_fragment_box (void *cls, const struct TransportFragmentBoxMessage *fb) { uint16_t size = ntohs (fb->header.size); - uint16_t bsize = size - sizeof (*fb); + uint16_t bsize = size - sizeof(*fb); + (void) cls; if (0 == bsize) { GNUNET_break_op (0); @@ -4095,32 +5330,159 @@ check_fragment_box (void *cls, const struct TransportFragmentBox *fb) /** - * Generate a fragment acknowledgement for an @a rc. + * Clean up an idle cummulative acknowledgement data structure. + * + * @param cls a `struct AcknowledgementCummulator *` + */ +static void +destroy_ack_cummulator (void *cls) +{ + struct AcknowledgementCummulator *ac = cls; + + ac->task = NULL; + GNUNET_assert (0 == ac->num_acks); + GNUNET_assert ( + GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (ack_cummulators, &ac->target, ac)); + GNUNET_free (ac); +} + + +/** + * Do the transmission of a cummulative acknowledgement now. + * + * @param cls a `struct AcknowledgementCummulator *` + */ +static void +transmit_cummulative_ack_cb (void *cls) +{ + struct AcknowledgementCummulator *ac = cls; + char buf[sizeof(struct TransportReliabilityAckMessage) + + ac->ack_counter + * sizeof(struct TransportCummulativeAckPayloadP)] GNUNET_ALIGN; + struct TransportReliabilityAckMessage *ack = + (struct TransportReliabilityAckMessage *) buf; + struct TransportCummulativeAckPayloadP *ap; + + ac->task = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending ACK with %u components to %s\n", + ac->ack_counter, + GNUNET_i2s (&ac->target)); + GNUNET_assert (0 < ac->ack_counter); + ack->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_ACK); + ack->header.size = + htons (sizeof(*ack) + + ac->ack_counter * sizeof(struct TransportCummulativeAckPayloadP)); + ack->ack_counter = htonl (ac->ack_counter++); + ap = (struct TransportCummulativeAckPayloadP *) &ack[1]; + for (unsigned int i = 0; i < ac->ack_counter; i++) + { + ap[i].ack_uuid = ac->ack_uuids[i].ack_uuid; + ap[i].ack_delay = GNUNET_TIME_relative_hton ( + GNUNET_TIME_absolute_get_duration (ac->ack_uuids[i].receive_time)); + } + route_control_message_without_fc (&ac->target, &ack->header, RMO_DV_ALLOWED); + ac->num_acks = 0; + ac->task = GNUNET_SCHEDULER_add_delayed (ACK_CUMMULATOR_TIMEOUT, + &destroy_ack_cummulator, + ac); +} + + +/** + * Transmit an acknowledgement for @a ack_uuid to @a pid delaying + * transmission by at most @a ack_delay. + * + * @param pid target peer + * @param ack_uuid UUID to ack + * @param max_delay how long can the ACK wait + */ +static void +cummulative_ack (const struct GNUNET_PeerIdentity *pid, + const struct AcknowledgementUUIDP *ack_uuid, + struct GNUNET_TIME_Absolute max_delay) +{ + struct AcknowledgementCummulator *ac; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Scheduling ACK %s for transmission to %s\n", + GNUNET_uuid2s (&ack_uuid->value), + GNUNET_i2s (pid)); + ac = GNUNET_CONTAINER_multipeermap_get (ack_cummulators, pid); + if (NULL == ac) + { + ac = GNUNET_new (struct AcknowledgementCummulator); + ac->target = *pid; + ac->min_transmission_time = max_delay; + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put ( + ack_cummulators, + &ac->target, + ac, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + } + else + { + if (MAX_CUMMULATIVE_ACKS == ac->num_acks) + { + /* must run immediately, ack buffer full! */ + GNUNET_SCHEDULER_cancel (ac->task); + transmit_cummulative_ack_cb (ac); + } + GNUNET_SCHEDULER_cancel (ac->task); + ac->min_transmission_time = + GNUNET_TIME_absolute_min (ac->min_transmission_time, max_delay); + } + GNUNET_assert (ac->num_acks < MAX_CUMMULATIVE_ACKS); + ac->ack_uuids[ac->num_acks].receive_time = GNUNET_TIME_absolute_get (); + ac->ack_uuids[ac->num_acks].ack_uuid = *ack_uuid; + ac->num_acks++; + ac->task = GNUNET_SCHEDULER_add_at (ac->min_transmission_time, + &transmit_cummulative_ack_cb, + ac); +} + + +/** + * Closure for #find_by_message_uuid. + */ +struct FindByMessageUuidContext +{ + /** + * UUID to look for. + */ + struct MessageUUIDP message_uuid; + + /** + * Set to the reassembly context if found. + */ + struct ReassemblyContext *rc; +}; + + +/** + * Iterator called to find a reassembly context by the message UUID in the + * multihashmap32. * - * @param rc context to generate ACK for, @a rc ACK state is reset + * @param cls a `struct FindByMessageUuidContext` + * @param key a key (unused) + * @param value a `struct ReassemblyContext` + * @return #GNUNET_YES if not found, #GNUNET_NO if found */ -static void -send_fragment_ack (struct ReassemblyContext *rc) +static int +find_by_message_uuid (void *cls, uint32_t key, void *value) { - struct TransportFragmentAckMessage *ack; + struct FindByMessageUuidContext *fc = cls; + struct ReassemblyContext *rc = value; - ack = GNUNET_new (struct TransportFragmentAckMessage); - ack->header.size = htons (sizeof (struct TransportFragmentAckMessage)); - ack->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT_ACK); - ack->frag_uuid = htonl (rc->frag_uuid); - ack->extra_acks = GNUNET_htonll (rc->extra_acks); - ack->msg_uuid = rc->msg_uuid; - ack->avg_ack_delay = GNUNET_TIME_relative_hton (rc->avg_ack_delay); - if (0 == rc->msg_missing) - ack->reassembly_timeout = GNUNET_TIME_relative_hton ( - GNUNET_TIME_UNIT_FOREVER_REL); /* signal completion */ - else - ack->reassembly_timeout = GNUNET_TIME_relative_hton ( - GNUNET_TIME_absolute_get_remaining (rc->reassembly_timeout)); - route_message (&rc->neighbour->pid, &ack->header, RMO_DV_ALLOWED); - rc->avg_ack_delay = GNUNET_TIME_UNIT_ZERO; - rc->num_acks = 0; - rc->extra_acks = 0LLU; + (void) key; + if (0 == GNUNET_memcmp (&fc->message_uuid, &rc->msg_uuid)) + { + fc->rc = rc; + return GNUNET_NO; + } + return GNUNET_YES; } @@ -4132,7 +5494,7 @@ send_fragment_ack (struct ReassemblyContext *rc) * @param fb the message that was received */ static void -handle_fragment_box (void *cls, const struct TransportFragmentBox *fb) +handle_fragment_box (void *cls, const struct TransportFragmentBoxMessage *fb) { struct CommunicatorMessageContext *cmc = cls; struct Neighbour *n; @@ -4141,12 +5503,11 @@ handle_fragment_box (void *cls, const struct TransportFragmentBox *fb) uint16_t msize; uint16_t fsize; uint16_t frag_off; - uint32_t frag_uuid; char *target; struct GNUNET_TIME_Relative cdelay; - int ack_now; + struct FindByMessageUuidContext fc; - n = GNUNET_CONTAINER_multipeermap_get (neighbours, &cmc->im.sender); + n = lookup_neighbour (&cmc->im.sender); if (NULL == n) { struct GNUNET_SERVICE_Client *client = cmc->tc->client; @@ -4158,7 +5519,7 @@ handle_fragment_box (void *cls, const struct TransportFragmentBox *fb) } if (NULL == n->reassembly_map) { - n->reassembly_map = GNUNET_CONTAINER_multishortmap_create (8, GNUNET_YES); + n->reassembly_map = GNUNET_CONTAINER_multihashmap32_create (8); n->reassembly_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); n->reassembly_timeout_task = @@ -4167,11 +5528,16 @@ handle_fragment_box (void *cls, const struct TransportFragmentBox *fb) n); } msize = ntohs (fb->msg_size); - rc = GNUNET_CONTAINER_multishortmap_get (n->reassembly_map, &fb->msg_uuid); - if (NULL == rc) - { - rc = GNUNET_malloc (sizeof (*rc) + msize + /* reassembly payload buffer */ - (msize + 7) / 8 * sizeof (uint8_t) /* bitfield */); + fc.message_uuid = fb->msg_uuid; + fc.rc = NULL; + (void) GNUNET_CONTAINER_multihashmap32_get_multiple (n->reassembly_map, + fb->msg_uuid.uuid, + &find_by_message_uuid, + &fc); + if (NULL == (rc = fc.rc)) + { + rc = GNUNET_malloc (sizeof(*rc) + msize /* reassembly payload buffer */ + + (msize + 7) / 8 * sizeof(uint8_t) /* bitfield */); rc->msg_uuid = fb->msg_uuid; rc->neighbour = n; rc->msg_size = msize; @@ -4182,18 +5548,30 @@ handle_fragment_box (void *cls, const struct TransportFragmentBox *fb) rc, rc->reassembly_timeout.abs_value_us); GNUNET_assert (GNUNET_OK == - GNUNET_CONTAINER_multishortmap_put ( + GNUNET_CONTAINER_multihashmap32_put ( n->reassembly_map, - &rc->msg_uuid, + rc->msg_uuid.uuid, rc, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE)); target = (char *) &rc[1]; rc->bitfield = (uint8_t *) (target + rc->msg_size); rc->msg_missing = rc->msg_size; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received fragment at offset %u/%u from %s for NEW message %u\n", + ntohs (fb->frag_off), + msize, + GNUNET_i2s (&cmc->im.sender), + (unsigned int) fb->msg_uuid.uuid); } else { target = (char *) &rc[1]; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received fragment at offset %u/%u from %s for message %u\n", + ntohs (fb->frag_off), + msize, + GNUNET_i2s (&cmc->im.sender), + (unsigned int) fb->msg_uuid.uuid); } if (msize != rc->msg_size) { @@ -4203,8 +5581,21 @@ handle_fragment_box (void *cls, const struct TransportFragmentBox *fb) } /* reassemble */ - fsize = ntohs (fb->header.size) - sizeof (*fb); + fsize = ntohs (fb->header.size) - sizeof(*fb); + if (0 == fsize) + { + GNUNET_break (0); + finish_cmc_handling (cmc); + return; + } frag_off = ntohs (fb->frag_off); + if (frag_off + fsize > msize) + { + /* Fragment (plus fragment size) exceeds message size! */ + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } memcpy (&target[frag_off], &fb[1], fsize); /* update bitfield and msg_missing */ for (unsigned int i = frag_off; i < frag_off + fsize; i++) @@ -4217,60 +5608,17 @@ handle_fragment_box (void *cls, const struct TransportFragmentBox *fb) } /* Compute cummulative ACK */ - frag_uuid = ntohl (fb->frag_uuid); cdelay = GNUNET_TIME_absolute_get_duration (rc->last_frag); - cdelay = GNUNET_TIME_relative_multiply (cdelay, rc->num_acks); + cdelay = GNUNET_TIME_relative_multiply (cdelay, rc->msg_missing / fsize); + if (0 == rc->msg_missing) + cdelay = GNUNET_TIME_UNIT_ZERO; + cummulative_ack (&cmc->im.sender, + &fb->ack_uuid, + GNUNET_TIME_relative_to_absolute (cdelay)); rc->last_frag = GNUNET_TIME_absolute_get (); - rc->avg_ack_delay = GNUNET_TIME_relative_add (rc->avg_ack_delay, cdelay); - ack_now = GNUNET_NO; - if (0 == rc->num_acks) - { - /* case one: first ack */ - rc->frag_uuid = frag_uuid; - rc->extra_acks = 0LLU; - rc->num_acks = 1; - } - else if ((frag_uuid >= rc->frag_uuid) && (frag_uuid <= rc->frag_uuid + 64)) - { - /* case two: ack fits after existing min UUID */ - if ((frag_uuid == rc->frag_uuid) || - (0 != (rc->extra_acks & (1LLU << (frag_uuid - rc->frag_uuid - 1))))) - { - /* duplicate fragment, ack now! */ - ack_now = GNUNET_YES; - } - else - { - rc->extra_acks |= (1LLU << (frag_uuid - rc->frag_uuid - 1)); - rc->num_acks++; - } - } - else if ((rc->frag_uuid > frag_uuid) && - (((rc->frag_uuid == frag_uuid + 64) && (0 == rc->extra_acks)) || - ((rc->frag_uuid < frag_uuid + 64) && - (rc->extra_acks == - (rc->extra_acks & - ~((1LLU << (64 - (rc->frag_uuid - frag_uuid))) - 1LLU)))))) - { - /* can fit ack by shifting extra acks and starting at - frag_uid, test above esured that the bits we will - shift 'extra_acks' by are all zero. */ - rc->extra_acks <<= (rc->frag_uuid - frag_uuid); - rc->extra_acks |= (1LLU << (rc->frag_uuid - frag_uuid - 1)); - rc->frag_uuid = frag_uuid; - rc->num_acks++; - } - if (65 == rc->num_acks) /* OPTIMIZE-FIXME: maybe use smaller threshold? This - is very aggressive. */ - ack_now = GNUNET_YES; /* maximum acks received */ - // FIXME: possibly also ACK based on RTT (but for that we'd need to - // determine the queue used for the ACK first!) - /* is reassembly complete? */ if (0 != rc->msg_missing) { - if (ack_now) - send_fragment_ack (rc); finish_cmc_handling (cmc); return; } @@ -4284,7 +5632,11 @@ handle_fragment_box (void *cls, const struct TransportFragmentBox *fb) return; } /* successful reassembly */ - send_fragment_ack (rc); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Fragment reassembly complete for message %u\n", + (unsigned int) fb->msg_uuid.uuid); + /* FIXME: check that the resulting msg is NOT a + DV Box or Reliability Box, as that is NOT allowed! */ demultiplex_with_cmc (cmc, msg); /* FIXME-OPTIMIZE: really free here? Might be bad if fragments are still en-route and we forget that we finished this reassembly immediately! @@ -4295,171 +5647,255 @@ handle_fragment_box (void *cls, const struct TransportFragmentBox *fb) /** - * Check the @a fa against the fragments associated with @a pm. - * If it matches, remove the matching fragments from the transmission - * list. + * Communicator gave us a reliability box. Check the message. * - * @param pm pending message to check against the ack - * @param fa the ack that was received - * @return #GNUNET_YES if @a fa matched, #GNUNET_NO if not + * @param cls a `struct CommunicatorMessageContext` + * @param rb the send message that was sent + * @return #GNUNET_YES if message is well-formed */ static int -check_ack_against_pm (struct PendingMessage *pm, - const struct TransportFragmentAckMessage *fa) -{ - int match; - struct PendingMessage *nxt; - uint32_t fs = ntohl (fa->frag_uuid); - uint64_t xtra = GNUNET_ntohll (fa->extra_acks); - - match = GNUNET_NO; - for (struct PendingMessage *frag = pm->head_frag; NULL != frag; frag = nxt) - { - const struct TransportFragmentBox *tfb = - (const struct TransportFragmentBox *) &pm[1]; - uint32_t fu = ntohl (tfb->frag_uuid); - - GNUNET_assert (PMT_FRAGMENT_BOX == frag->pmt); - nxt = frag->next_frag; - /* Check for exact match or match in the 'xtra' bitmask */ - if ((fu == fs) || - ((fu > fs) && (fu <= fs + 64) && (0 != (1LLU << (fu - fs - 1) & xtra)))) - { - match = GNUNET_YES; - free_fragment_tree (frag); - } - } - return match; +check_reliability_box (void *cls, + const struct TransportReliabilityBoxMessage *rb) +{ + (void) cls; + GNUNET_MQ_check_boxed_message (rb); + return GNUNET_YES; } /** - * Communicator gave us a fragment acknowledgement. Process the request. + * Communicator gave us a reliability box. Process the request. * * @param cls a `struct CommunicatorMessageContext` (must call * #finish_cmc_handling() when done) - * @param fa the message that was received + * @param rb the message that was received */ static void -handle_fragment_ack (void *cls, const struct TransportFragmentAckMessage *fa) +handle_reliability_box (void *cls, + const struct TransportReliabilityBoxMessage *rb) { struct CommunicatorMessageContext *cmc = cls; - struct Neighbour *n; - int matched; + const struct GNUNET_MessageHeader *inbox = + (const struct GNUNET_MessageHeader *) &rb[1]; + struct GNUNET_TIME_Relative rtt; - n = GNUNET_CONTAINER_multipeermap_get (neighbours, &cmc->im.sender); - if (NULL == n) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received reliability box from %s with UUID %s of type %u\n", + GNUNET_i2s (&cmc->im.sender), + GNUNET_uuid2s (&rb->ack_uuid.value), + (unsigned int) ntohs (inbox->type)); + rtt = GNUNET_TIME_UNIT_SECONDS; /* FIXME: should base this on "RTT", but we + do not really have an RTT for the + * incoming* queue (should we have + the sender add it to the rb message?) */ + cummulative_ack ( + &cmc->im.sender, + &rb->ack_uuid, + (0 == ntohl (rb->ack_countdown)) + ? GNUNET_TIME_UNIT_ZERO_ABS + : GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_divide (rtt, 8 /* FIXME: magic constant */))); + /* continue with inner message */ + /* FIXME: check that inbox is NOT a DV Box, fragment or another + reliability box (not allowed!) */ + demultiplex_with_cmc (cmc, inbox); +} + + +/** + * Check if we have advanced to another age since the last time. If + * so, purge ancient statistics (more than GOODPUT_AGING_SLOTS before + * the current age) + * + * @param pd[in,out] data to update + * @param age current age + */ +static void +update_pd_age (struct PerformanceData *pd, unsigned int age) +{ + unsigned int sage; + + if (age == pd->last_age) + return; /* nothing to do */ + sage = GNUNET_MAX (pd->last_age, age - 2 * GOODPUT_AGING_SLOTS); + for (unsigned int i = sage; i <= age - GOODPUT_AGING_SLOTS; i++) { - struct GNUNET_SERVICE_Client *client = cmc->tc->client; + struct TransmissionHistoryEntry *the = &pd->the[i % GOODPUT_AGING_SLOTS]; - GNUNET_break (0); - finish_cmc_handling (cmc); - GNUNET_SERVICE_client_drop (client); - return; + the->bytes_sent = 0; + the->bytes_received = 0; } - /* FIXME-OPTIMIZE: maybe use another hash map here? */ - matched = GNUNET_NO; - for (struct PendingMessage *pm = n->pending_msg_head; NULL != pm; - pm = pm->prev_neighbour) + pd->last_age = age; +} + + +/** + * Update @a pd based on the latest @a rtt and the number of bytes + * that were confirmed to be successfully transmitted. + * + * @param pd[in,out] data to update + * @param rtt latest round-trip time + * @param bytes_transmitted_ok number of bytes receiver confirmed as received + */ +static void +update_performance_data (struct PerformanceData *pd, + struct GNUNET_TIME_Relative rtt, + uint16_t bytes_transmitted_ok) +{ + uint64_t nval = rtt.rel_value_us; + uint64_t oval = pd->aged_rtt.rel_value_us; + unsigned int age = get_age (); + struct TransmissionHistoryEntry *the = &pd->the[age % GOODPUT_AGING_SLOTS]; + + if (oval == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) + pd->aged_rtt = rtt; + else + pd->aged_rtt.rel_value_us = (nval + 7 * oval) / 8; + update_pd_age (pd, age); + the->bytes_received += bytes_transmitted_ok; +} + + +/** + * We have successfully transmitted data via @a q, update metrics. + * + * @param q queue to update + * @param rtt round trip time observed + * @param bytes_transmitted_ok number of bytes successfully transmitted + */ +static void +update_queue_performance (struct Queue *q, + struct GNUNET_TIME_Relative rtt, + uint16_t bytes_transmitted_ok) +{ + update_performance_data (&q->pd, rtt, bytes_transmitted_ok); +} + + +/** + * We have successfully transmitted data via @a dvh, update metrics. + * + * @param dvh distance vector path data to update + * @param rtt round trip time observed + * @param bytes_transmitted_ok number of bytes successfully transmitted + */ +static void +update_dvh_performance (struct DistanceVectorHop *dvh, + struct GNUNET_TIME_Relative rtt, + uint16_t bytes_transmitted_ok) +{ + update_performance_data (&dvh->pd, rtt, bytes_transmitted_ok); +} + + +/** + * We have completed transmission of @a pm, remove it from + * the transmission queues (and if it is a fragment, continue + * up the tree as necessary). + * + * @param pm pending message that was transmitted + */ +static void +completed_pending_message (struct PendingMessage *pm) +{ + struct PendingMessage *pos; + + switch (pm->pmt) { - if (0 != GNUNET_memcmp (&fa->msg_uuid, &pm->msg_uuid)) - continue; - matched = GNUNET_YES; - if (GNUNET_YES == check_ack_against_pm (pm, fa)) - { - struct GNUNET_TIME_Relative avg_ack_delay = - GNUNET_TIME_relative_ntoh (fa->avg_ack_delay); - // FIXME: update RTT and other reliability data! - // ISSUE: we don't know which of n's queues the message(s) - // took (and in fact the different messages might have gone - // over different queues and possibly over multiple). - // => track queues with PendingMessages, and update RTT only if - // the queue used is unique? - // -> how can we get loss rates? - // -> or, add extra state to Box and ACK to identify queue? - // IDEA: generate MULTIPLE frag-uuids per fragment and track - // the queue with the fragment! (-> this logic must - // be moved into check_ack_against_pm!) - (void) avg_ack_delay; - } - else - { - GNUNET_STATISTICS_update (GST_stats, - "# FRAGMENT_ACKS dropped, no matching fragment", - 1, - GNUNET_NO); - } - if (NULL == pm->head_frag) - { - // if entire message is ACKed, handle that as well. - // => clean up PM, any post actions? - free_pending_message (pm); - } - else + case PMT_CORE: + case PMT_RELIABILITY_BOX: + /* Full message sent, we are done */ + client_send_response (pm); + return; + + case PMT_FRAGMENT_BOX: + /* Fragment sent over reliabile channel */ + free_fragment_tree (pm); + pos = pm->frag_parent; + GNUNET_CONTAINER_MDLL_remove (frag, pos->head_frag, pos->tail_frag, pm); + GNUNET_free (pm); + /* check if subtree is done */ + while ((NULL == pos->head_frag) && (pos->frag_off == pos->bytes_msg) && + (pos != pm)) { - struct GNUNET_TIME_Relative reassembly_timeout = - GNUNET_TIME_relative_ntoh (fa->reassembly_timeout); - // OPTIMIZE-FIXME: adjust retransmission strategy based on - // reassembly_timeout! - (void) reassembly_timeout; + pm = pos; + pos = pm->frag_parent; + GNUNET_CONTAINER_MDLL_remove (frag, pos->head_frag, pos->tail_frag, pm); + GNUNET_free (pm); } - break; - } - if (GNUNET_NO == matched) - { - GNUNET_STATISTICS_update (GST_stats, - "# FRAGMENT_ACKS dropped, no matching pending message", - 1, - GNUNET_NO); + + /* Was this the last applicable fragmment? */ + if ((NULL == pos->head_frag) && (NULL == pos->frag_parent) && + (pos->frag_off == pos->bytes_msg)) + client_send_response (pos); + return; + + case PMT_DV_BOX: + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Completed transmission of message %llu (DV Box)\n", + pm->logging_uuid); + free_pending_message (pm); + return; } - finish_cmc_handling (cmc); } /** - * Communicator gave us a reliability box. Check the message. + * The @a pa was acknowledged, process the acknowledgement. * - * @param cls a `struct CommunicatorMessageContext` - * @param rb the send message that was sent - * @return #GNUNET_YES if message is well-formed + * @param pa the pending acknowledgement that was satisfied + * @param ack_delay artificial delay from cummulative acks created by the + * other peer */ -static int -check_reliability_box (void *cls, const struct TransportReliabilityBox *rb) +static void +handle_acknowledged (struct PendingAcknowledgement *pa, + struct GNUNET_TIME_Relative ack_delay) { - GNUNET_MQ_check_boxed_message (rb); - return GNUNET_YES; + struct GNUNET_TIME_Relative delay; + + delay = GNUNET_TIME_absolute_get_duration (pa->transmission_time); + if (delay.rel_value_us > ack_delay.rel_value_us) + delay = GNUNET_TIME_UNIT_ZERO; + else + delay = GNUNET_TIME_relative_subtract (delay, ack_delay); + if (NULL != pa->queue) + update_queue_performance (pa->queue, delay, pa->message_size); + if (NULL != pa->dvh) + update_dvh_performance (pa->dvh, delay, pa->message_size); + if (NULL != pa->pm) + completed_pending_message (pa->pm); + free_pending_acknowledgement (pa); } /** - * Communicator gave us a reliability box. Process the request. + * Communicator gave us a reliability ack. Check it is well-formed. * - * @param cls a `struct CommunicatorMessageContext` (must call - * #finish_cmc_handling() when done) - * @param rb the message that was received + * @param cls a `struct CommunicatorMessageContext` (unused) + * @param ra the message that was received + * @return #GNUNET_Ok if @a ra is well-formed */ -static void -handle_reliability_box (void *cls, const struct TransportReliabilityBox *rb) +static int +check_reliability_ack (void *cls, + const struct TransportReliabilityAckMessage *ra) { - struct CommunicatorMessageContext *cmc = cls; - const struct GNUNET_MessageHeader *inbox = - (const struct GNUNET_MessageHeader *) &rb[1]; + unsigned int n_acks; - if (0 == ntohl (rb->ack_countdown)) + (void) cls; + n_acks = (ntohs (ra->header.size) - sizeof(*ra)) + / sizeof(struct TransportCummulativeAckPayloadP); + if (0 == n_acks) { - struct TransportReliabilityAckMessage *ack; - - /* FIXME-OPTIMIZE: implement cummulative ACKs and ack_countdown, - then setting the avg_ack_delay field below: */ - ack = GNUNET_malloc (sizeof (*ack) + sizeof (struct GNUNET_ShortHashCode)); - ack->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_ACK); - ack->header.size = - htons (sizeof (*ack) + sizeof (struct GNUNET_ShortHashCode)); - memcpy (&ack[1], &rb->msg_uuid, sizeof (struct GNUNET_ShortHashCode)); - route_message (&cmc->im.sender, &ack->header, RMO_DV_ALLOWED); + GNUNET_break_op (0); + return GNUNET_SYSERR; } - /* continue with inner message */ - demultiplex_with_cmc (cmc, inbox); + if ((ntohs (ra->header.size) - sizeof(*ra)) != + n_acks * sizeof(struct TransportCummulativeAckPayloadP)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; } @@ -4475,70 +5911,41 @@ handle_reliability_ack (void *cls, const struct TransportReliabilityAckMessage *ra) { struct CommunicatorMessageContext *cmc = cls; - struct Neighbour *n; + const struct TransportCummulativeAckPayloadP *ack; unsigned int n_acks; - const struct GNUNET_ShortHashCode *msg_uuids; - struct PendingMessage *nxt; - int matched; - - n = GNUNET_CONTAINER_multipeermap_get (neighbours, &cmc->im.sender); - if (NULL == n) - { - struct GNUNET_SERVICE_Client *client = cmc->tc->client; - - GNUNET_break (0); - finish_cmc_handling (cmc); - GNUNET_SERVICE_client_drop (client); - return; - } - n_acks = (ntohs (ra->header.size) - sizeof (*ra)) / - sizeof (struct GNUNET_ShortHashCode); - msg_uuids = (const struct GNUNET_ShortHashCode *) &ra[1]; + uint32_t ack_counter; - /* FIXME-OPTIMIZE: maybe use another hash map here? */ - matched = GNUNET_NO; - for (struct PendingMessage *pm = n->pending_msg_head; NULL != pm; pm = nxt) + n_acks = (ntohs (ra->header.size) - sizeof(*ra)) + / sizeof(struct TransportCummulativeAckPayloadP); + ack = (const struct TransportCummulativeAckPayloadP *) &ra[1]; + for (unsigned int i = 0; i < n_acks; i++) { - int in_list; - - nxt = pm->next_neighbour; - in_list = GNUNET_NO; - for (unsigned int i = 0; i < n_acks; i++) + struct PendingAcknowledgement *pa = + GNUNET_CONTAINER_multiuuidmap_get (pending_acks, &ack[i].ack_uuid.value); + if (NULL == pa) { - if (0 != GNUNET_memcmp (&msg_uuids[i], &pm->msg_uuid)) - continue; - in_list = GNUNET_YES; - break; - } - if (GNUNET_NO == in_list) + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received ACK from %s with UUID %s which is unknown to us!\n", + GNUNET_i2s (&cmc->im.sender), + GNUNET_uuid2s (&ack[i].ack_uuid.value)); + GNUNET_STATISTICS_update ( + GST_stats, + "# FRAGMENT_ACKS dropped, no matching pending message", + 1, + GNUNET_NO); continue; - - /* this pm was acked! */ - matched = GNUNET_YES; - free_pending_message (pm); - - { - struct GNUNET_TIME_Relative avg_ack_delay = - GNUNET_TIME_relative_ntoh (ra->avg_ack_delay); - // FIXME: update RTT and other reliability data! - // ISSUE: we don't know which of n's queues the message(s) - // took (and in fact the different messages might have gone - // over different queues and possibly over multiple). - // => track queues with PendingMessages, and update RTT only if - // the queue used is unique? - // -> how can we get loss rates? - // -> or, add extra state to MSG and ACKs to identify queue? - // -> if we do this, might just do the same for the avg_ack_delay! - (void) avg_ack_delay; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received ACK from %s with UUID %s\n", + GNUNET_i2s (&cmc->im.sender), + GNUNET_uuid2s (&ack[i].ack_uuid.value)); + handle_acknowledged (pa, GNUNET_TIME_relative_ntoh (ack[i].ack_delay)); } - if (GNUNET_NO == matched) - { - GNUNET_STATISTICS_update (GST_stats, - "# FRAGMENT_ACKS dropped, no matching pending message", - 1, - GNUNET_NO); - } + + ack_counter = htonl (ra->ack_counter); + (void) ack_counter; /* silence compiler warning for now */ + // FIXME-OPTIMIZE: track ACK losses based on ack_counter somewhere! + // (DV and/or Neighbour?) finish_cmc_handling (cmc); } @@ -4555,12 +5962,22 @@ check_backchannel_encapsulation ( void *cls, const struct TransportBackchannelEncapsulationMessage *be) { - uint16_t size = ntohs (be->header.size); + uint16_t size = ntohs (be->header.size) - sizeof(*be); + const struct GNUNET_MessageHeader *inbox = + (const struct GNUNET_MessageHeader *) &be[1]; + const char *is; + uint16_t isize; (void) cls; - if ((size - sizeof (*be)) < - (sizeof (struct TransportBackchannelRequestPayload) + - sizeof (struct GNUNET_MessageHeader))) + if (ntohs (inbox->size) >= size) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + isize = ntohs (inbox->size); + is = ((const char *) inbox) + isize; + size -= isize; + if ('\0' != is[size - 1]) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -4571,8 +5988,7 @@ check_backchannel_encapsulation ( /** * Communicator gave us a backchannel encapsulation. Process the request. - * (We are not the origin of the backchannel here, the communicator simply - * received a backchannel message and we are expected to forward it.) + * (We are the destination of the backchannel here.) * * @param cls a `struct CommunicatorMessageContext` (must call * #finish_cmc_handling() when done) @@ -4584,50 +6000,45 @@ handle_backchannel_encapsulation ( const struct TransportBackchannelEncapsulationMessage *be) { struct CommunicatorMessageContext *cmc = cls; - struct BackchannelKeyState key; - struct GNUNET_HashCode hmac; - const char *hdr; - size_t hdr_len; - - if (0 != GNUNET_memcmp (&be->target, &GST_my_identity)) - { - /* not for me, try to route to target */ - route_message (&be->target, - GNUNET_copy_message (&be->header), - RMO_DV_ALLOWED); - finish_cmc_handling (cmc); - return; - } - dh_key_derive_eph_pub (&be->ephemeral_key, &be->iv, &key); - hdr = (const char *) &be[1]; - hdr_len = ntohs (be->header.size) - sizeof (*be); - bc_hmac (&key, &hmac, hdr, hdr_len); - if (0 != GNUNET_memcmp (&hmac, &be->hmac)) - { - /* HMAC missmatch, disard! */ - GNUNET_break_op (0); - finish_cmc_handling (cmc); - return; - } - /* begin actual decryption */ + struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming *cbi; + struct GNUNET_MQ_Envelope *env; + struct TransportClient *tc; + const struct GNUNET_MessageHeader *inbox = + (const struct GNUNET_MessageHeader *) &be[1]; + uint16_t isize = ntohs (inbox->size); + const char *target_communicator = ((const char *) inbox) + isize; + + /* Find client providing this communicator */ + for (tc = clients_head; NULL != tc; tc = tc->next) + if ((CT_COMMUNICATOR == tc->type) && + (0 == + strcmp (tc->details.communicator.address_prefix, target_communicator))) + break; + if (NULL == tc) { - struct TransportBackchannelRequestPayload ppay; - char body[hdr_len - sizeof (ppay)]; + char *stastr; - GNUNET_assert (hdr_len >= - sizeof (ppay) + sizeof (struct GNUNET_MessageHeader)); - bc_decrypt (&key, &ppay, hdr, sizeof (ppay)); - bc_decrypt (&key, &body, &hdr[sizeof (ppay)], hdr_len - sizeof (ppay)); - bc_key_clean (&key); - // FIXME: verify signatures in ppay! - // => check if ephemeral key is known & valid, if not - // => verify sig, cache ephemeral key - // => update monotonic_time of sender for replay detection - - // FIXME: forward to specified communicator! - // (using GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL_INCOMING) + GNUNET_asprintf ( + &stastr, + "# Backchannel message dropped: target communicator `%s' unknown", + target_communicator); + GNUNET_STATISTICS_update (GST_stats, stastr, 1, GNUNET_NO); + GNUNET_free (stastr); + return; } - finish_cmc_handling (cmc); + /* Finally, deliver backchannel message to communicator */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Delivering backchannel message from %s of type %u to %s\n", + GNUNET_i2s (&cmc->im.sender), + ntohs (inbox->type), + target_communicator); + env = GNUNET_MQ_msg_extra ( + cbi, + isize, + GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL_INCOMING); + cbi->pid = cmc->im.sender; + memcpy (&cbi[1], inbox, isize); + GNUNET_MQ_send (tc->mq, env); } @@ -4649,52 +6060,18 @@ path_cleanup_cb (void *cls) dv->timeout_task = NULL; while (NULL != (pos = dv->dv_head)) { - GNUNET_assert (dv == pos->dv); - if (GNUNET_TIME_absolute_get_remaining (pos->timeout).rel_value_us > 0) - break; - free_distance_vector_hop (pos); - } - if (NULL == pos) - { - free_dv_route (dv); - return; - } - dv->timeout_task = - GNUNET_SCHEDULER_add_at (pos->timeout, &path_cleanup_cb, dv); -} - -/** - * Task run to check whether the hops of the @a cls still - * are validated, or if we need to core about disconnection. - * - * @param cls a `struct DistanceVector` (with core_visible set!) - */ -static void -check_dv_path_down (void *cls) -{ - struct DistanceVector *dv = cls; - struct Neighbour *n; - - dv->visibility_task = NULL; - GNUNET_assert (GNUNET_YES == dv->core_visible); - for (struct DistanceVectorHop *pos = dv->dv_head; NULL != pos; - pos = pos->next_dv) - { - if (0 < - GNUNET_TIME_absolute_get_remaining (pos->path_valid_until).rel_value_us) - { - dv->visibility_task = GNUNET_SCHEDULER_add_at (pos->path_valid_until, - &check_dv_path_down, - dv); - return; - } + GNUNET_assert (dv == pos->dv); + if (GNUNET_TIME_absolute_get_remaining (pos->timeout).rel_value_us > 0) + break; + free_distance_vector_hop (pos); } - /* all paths invalid, make dv core-invisible */ - dv->core_visible = GNUNET_NO; - n = GNUNET_CONTAINER_multipeermap_get (neighbours, &dv->target); - if ((NULL != n) && (GNUNET_YES == n->core_visible)) - return; /* no need to tell core, connection still up! */ - cores_send_disconnect_info (&dv->target); + if (NULL == pos) + { + free_dv_route (dv); + return; + } + dv->timeout_task = + GNUNET_SCHEDULER_add_at (pos->timeout, &path_cleanup_cb, dv); } @@ -4709,22 +6086,41 @@ static void activate_core_visible_dv_path (struct DistanceVectorHop *hop) { struct DistanceVector *dv = hop->dv; - struct Neighbour *n; + struct VirtualLink *vl; - GNUNET_assert (GNUNET_NO == dv->core_visible); - GNUNET_assert (NULL == dv->visibility_task); - - dv->core_visible = GNUNET_YES; - dv->visibility_task = - GNUNET_SCHEDULER_add_at (hop->path_valid_until, &check_dv_path_down, dv); - n = GNUNET_CONTAINER_multipeermap_get (neighbours, &dv->target); - if ((NULL != n) && (GNUNET_YES == n->core_visible)) - return; /* no need to tell core, connection already up! */ - cores_send_connect_info (&dv->target, - (NULL != n) - ? GNUNET_BANDWIDTH_value_sum (n->quota_out, - dv->quota_out) - : dv->quota_out); + vl = lookup_virtual_link (&dv->target); + if (NULL != vl) + { + /* Link was already up, remember dv is also now available and we are done */ + vl->dv = dv; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Virtual link to %s could now also use DV!\n", + GNUNET_i2s (&dv->target)); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating new virtual link to %s using DV!\n", + GNUNET_i2s (&dv->target)); + vl = GNUNET_new (struct VirtualLink); + vl->message_uuid_ctr = + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); + vl->target = dv->target; + vl->dv = dv; + dv->vl = vl; + vl->core_recv_window = RECV_WINDOW_SIZE; + vl->available_fc_window_size = DEFAULT_WINDOW_SIZE; + vl->visibility_task = + GNUNET_SCHEDULER_add_at (hop->path_valid_until, &check_link_down, vl); + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put ( + links, + &vl->target, + vl, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + consider_sending_fc (vl); + /* We lacked a confirmed connection to the target + before, so tell CORE about it (finally!) */ + cores_send_connect_info (&dv->target); } @@ -4740,13 +6136,13 @@ activate_core_visible_dv_path (struct DistanceVectorHop *hop) * non-first hop is in our neighbour list (returning #GNUNET_SYSERR). * * @param path the path we learned, path[0] should be us, - * and then path contains a valid path from us to `path[path_len-1]` - * path[1] should be a direct neighbour (we should check!) + * and then path contains a valid path from us to + * `path[path_len-1]` path[1] should be a direct neighbour (we should check!) * @param path_len number of entries on the @a path, at least three! * @param network_latency how long does the message take from us to * `path[path_len-1]`? set to "forever" if unknown - * @param path_valid_until how long is this path considered validated? Maybe be - * zero. + * @param path_valid_until how long is this path considered validated? Maybe + * be zero. * @return #GNUNET_YES on success, * #GNUNET_NO if we have better path(s) to the target * #GNUNET_SYSERR if the path is useless and/or invalid @@ -4771,7 +6167,7 @@ learn_dv_path (const struct GNUNET_PeerIdentity *path, return GNUNET_SYSERR; } GNUNET_assert (0 == GNUNET_memcmp (&GST_my_identity, &path[0])); - next_hop = GNUNET_CONTAINER_multipeermap_get (neighbours, &path[1]); + next_hop = lookup_neighbour (&path[1]); if (NULL == next_hop) { /* next hop must be a neighbour, otherwise this whole thing is useless! */ @@ -4779,11 +6175,20 @@ learn_dv_path (const struct GNUNET_PeerIdentity *path, return GNUNET_SYSERR; } for (unsigned int i = 2; i < path_len; i++) - if (NULL != GNUNET_CONTAINER_multipeermap_get (neighbours, &path[i])) + if (NULL != lookup_neighbour (&path[i])) { - /* Useless path, we have a direct connection to some hop - in the middle of the path, so this one doesn't even - seem terribly useful for redundancy */ + /* Useless path: we have a direct connection to some hop + in the middle of the path, so this one is not even + terribly useful for redundancy */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Path of %u hops useless: directly link to hop %u (%s)\n", + path_len, + i, + GNUNET_i2s (&path[i])); + GNUNET_STATISTICS_update (GST_stats, + "# Useless DV path ignored: hop is neighbour", + 1, + GNUNET_NO); return GNUNET_SYSERR; } dv = GNUNET_CONTAINER_multipeermap_get (dv_routes, &path[path_len - 1]); @@ -4839,19 +6244,23 @@ learn_dv_path (const struct GNUNET_PeerIdentity *path, GNUNET_TIME_absolute_max (pos->path_valid_until, path_valid_until); GNUNET_CONTAINER_MDLL_remove (dv, dv->dv_head, dv->dv_tail, pos); GNUNET_CONTAINER_MDLL_insert (dv, dv->dv_head, dv->dv_tail, pos); - if ((GNUNET_NO == dv->core_visible) && - (0 < GNUNET_TIME_absolute_get_remaining (path_valid_until) - .rel_value_us)) + if (0 < + GNUNET_TIME_absolute_get_remaining (path_valid_until).rel_value_us) activate_core_visible_dv_path (pos); if (last_timeout.rel_value_us < GNUNET_TIME_relative_subtract (DV_PATH_VALIDITY_TIMEOUT, DV_PATH_DISCOVERY_FREQUENCY) - .rel_value_us) + .rel_value_us) { /* Some peer send DV learn messages too often, we are learning the same path faster than it would be useful; do not forward! */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Rediscovered path too quickly, not forwarding further\n"); return GNUNET_NO; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Refreshed known path to %s, forwarding further\n", + GNUNET_i2s (&dv->target)); return GNUNET_YES; } } @@ -4861,27 +6270,33 @@ learn_dv_path (const struct GNUNET_PeerIdentity *path, if (shorter_distance >= MAX_DV_PATHS_TO_TARGET) { /* We have a shorter path already! */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Have many shorter DV paths %s, not forwarding further\n", + GNUNET_i2s (&dv->target)); return GNUNET_NO; } /* create new DV path entry */ - hop = GNUNET_malloc (sizeof (struct DistanceVectorHop) + - sizeof (struct GNUNET_PeerIdentity) * (path_len - 2)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Discovered new DV path to %s\n", + GNUNET_i2s (&dv->target)); + hop = GNUNET_malloc (sizeof(struct DistanceVectorHop) + + sizeof(struct GNUNET_PeerIdentity) * (path_len - 2)); hop->next_hop = next_hop; hop->dv = dv; hop->path = (const struct GNUNET_PeerIdentity *) &hop[1]; memcpy (&hop[1], &path[2], - sizeof (struct GNUNET_PeerIdentity) * (path_len - 2)); + sizeof(struct GNUNET_PeerIdentity) * (path_len - 2)); hop->timeout = GNUNET_TIME_relative_to_absolute (DV_PATH_VALIDITY_TIMEOUT); hop->path_valid_until = path_valid_until; hop->distance = path_len - 2; + hop->pd.aged_rtt = network_latency; GNUNET_CONTAINER_MDLL_insert (dv, dv->dv_head, dv->dv_tail, hop); GNUNET_CONTAINER_MDLL_insert (neighbour, next_hop->dv_head, next_hop->dv_tail, hop); - if ((GNUNET_NO == dv->core_visible) && - (0 < GNUNET_TIME_absolute_get_remaining (path_valid_until).rel_value_us)) + if (0 < GNUNET_TIME_absolute_get_remaining (path_valid_until).rel_value_us) activate_core_visible_dv_path (hop); return GNUNET_YES; } @@ -4895,14 +6310,14 @@ learn_dv_path (const struct GNUNET_PeerIdentity *path, * @return #GNUNET_YES if message is well-formed */ static int -check_dv_learn (void *cls, const struct TransportDVLearn *dvl) +check_dv_learn (void *cls, const struct TransportDVLearnMessage *dvl) { uint16_t size = ntohs (dvl->header.size); uint16_t num_hops = ntohs (dvl->num_hops); const struct DVPathEntryP *hops = (const struct DVPathEntryP *) &dvl[1]; (void) cls; - if (size != sizeof (*dvl) + num_hops * sizeof (struct DVPathEntryP)) + if (size != sizeof(*dvl) + num_hops * sizeof(struct DVPathEntryP)) { GNUNET_break_op (0); return GNUNET_SYSERR; @@ -4942,23 +6357,27 @@ check_dv_learn (void *cls, const struct TransportDVLearn *dvl) */ static void forward_dv_learn (const struct GNUNET_PeerIdentity *next_hop, - const struct TransportDVLearn *msg, + const struct TransportDVLearnMessage *msg, uint16_t bi_history, uint16_t nhops, const struct DVPathEntryP *hops, struct GNUNET_TIME_Absolute in_time) { struct DVPathEntryP *dhops; - struct TransportDVLearn *fwd; + char buf[sizeof(struct TransportDVLearnMessage) + + (nhops + 1) * sizeof(struct DVPathEntryP)] GNUNET_ALIGN; + struct TransportDVLearnMessage *fwd = (struct TransportDVLearnMessage *) buf; struct GNUNET_TIME_Relative nnd; /* compute message for forwarding */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Forwarding DV learn message originating from %s to %s\n", + GNUNET_i2s (&msg->initiator), + GNUNET_i2s2 (next_hop)); GNUNET_assert (nhops < MAX_DV_HOPS_ALLOWED); - fwd = GNUNET_malloc (sizeof (struct TransportDVLearn) + - (nhops + 1) * sizeof (struct DVPathEntryP)); fwd->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN); - fwd->header.size = htons (sizeof (struct TransportDVLearn) + - (nhops + 1) * sizeof (struct DVPathEntryP)); + fwd->header.size = htons (sizeof(struct TransportDVLearnMessage) + + (nhops + 1) * sizeof(struct DVPathEntryP)); fwd->num_hops = htons (nhops + 1); fwd->bidirectional = htons (bi_history); nnd = GNUNET_TIME_relative_add (GNUNET_TIME_absolute_get_duration (in_time), @@ -4969,28 +6388,31 @@ forward_dv_learn (const struct GNUNET_PeerIdentity *next_hop, fwd->initiator = msg->initiator; fwd->challenge = msg->challenge; dhops = (struct DVPathEntryP *) &fwd[1]; - GNUNET_memcpy (dhops, hops, sizeof (struct DVPathEntryP) * nhops); + GNUNET_memcpy (dhops, hops, sizeof(struct DVPathEntryP) * nhops); dhops[nhops].hop = GST_my_identity; { - struct DvHopPS dhp = {.purpose.purpose = - htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP), - .purpose.size = htonl (sizeof (dhp)), - .pred = dhops[nhops - 1].hop, - .succ = *next_hop, - .challenge = msg->challenge}; - - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, - &dhp.purpose, - &dhops[nhops].hop_sig)); - } - route_message (next_hop, &fwd->header, RMO_UNCONFIRMED_ALLOWED); + struct DvHopPS dhp = { + .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP), + .purpose.size = htonl (sizeof(dhp)), + .pred = dhops[nhops - 1].hop, + .succ = *next_hop, + .challenge = msg->challenge + }; + + GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, + &dhp, + &dhops[nhops].hop_sig); + } + route_control_message_without_fc (next_hop, + &fwd->header, + RMO_UNCONFIRMED_ALLOWED); } /** * Check signature of type #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR * + * @param sender_monotonic_time monotonic time of the initiator * @param init the signer * @param challenge the challenge that was signed * @param init_sig signature presumably by @a init @@ -4998,19 +6420,21 @@ forward_dv_learn (const struct GNUNET_PeerIdentity *next_hop, */ static int validate_dv_initiator_signature ( + struct GNUNET_TIME_AbsoluteNBO sender_monotonic_time, const struct GNUNET_PeerIdentity *init, - const struct GNUNET_ShortHashCode *challenge, + const struct ChallengeNonceP *challenge, const struct GNUNET_CRYPTO_EddsaSignature *init_sig) { - struct DvInitPS ip = {.purpose.purpose = htonl ( - GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR), - .purpose.size = htonl (sizeof (ip)), - .challenge = *challenge}; + struct DvInitPS ip = { .purpose.purpose = htonl ( + GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR), + .purpose.size = htonl (sizeof(ip)), + .monotonic_time = sender_monotonic_time, + .challenge = *challenge }; if ( GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR, - &ip.purpose, + &ip, init_sig, &init->public_key)) { @@ -5021,6 +6445,224 @@ validate_dv_initiator_signature ( } +/** + * Closure for #dv_neighbour_selection and #dv_neighbour_transmission. + */ +struct NeighbourSelectionContext +{ + /** + * Original message we received. + */ + const struct TransportDVLearnMessage *dvl; + + /** + * The hops taken. + */ + const struct DVPathEntryP *hops; + + /** + * Time we received the message. + */ + struct GNUNET_TIME_Absolute in_time; + + /** + * Offsets of the selected peers. + */ + uint32_t selections[MAX_DV_DISCOVERY_SELECTION]; + + /** + * Number of peers eligible for selection. + */ + unsigned int num_eligible; + + /** + * Number of peers that were selected for forwarding. + */ + unsigned int num_selections; + + /** + * Number of hops in @e hops + */ + uint16_t nhops; + + /** + * Bitmap of bidirectional connections encountered. + */ + uint16_t bi_history; +}; + + +/** + * Function called for each neighbour during #handle_dv_learn. + * + * @param cls a `struct NeighbourSelectionContext *` + * @param pid identity of the peer + * @param value a `struct Neighbour` + * @return #GNUNET_YES (always) + */ +static int +dv_neighbour_selection (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct NeighbourSelectionContext *nsc = cls; + + (void) value; + if (0 == GNUNET_memcmp (pid, &nsc->dvl->initiator)) + return GNUNET_YES; /* skip initiator */ + for (unsigned int i = 0; i < nsc->nhops; i++) + if (0 == GNUNET_memcmp (pid, &nsc->hops[i].hop)) + return GNUNET_YES; + /* skip peers on path */ + nsc->num_eligible++; + return GNUNET_YES; +} + + +/** + * Function called for each neighbour during #handle_dv_learn. + * We call #forward_dv_learn() on the neighbour(s) selected + * during #dv_neighbour_selection(). + * + * @param cls a `struct NeighbourSelectionContext *` + * @param pid identity of the peer + * @param value a `struct Neighbour` + * @return #GNUNET_YES (always) + */ +static int +dv_neighbour_transmission (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct NeighbourSelectionContext *nsc = cls; + + (void) value; + if (0 == GNUNET_memcmp (pid, &nsc->dvl->initiator)) + return GNUNET_YES; /* skip initiator */ + for (unsigned int i = 0; i < nsc->nhops; i++) + if (0 == GNUNET_memcmp (pid, &nsc->hops[i].hop)) + return GNUNET_YES; + /* skip peers on path */ + for (unsigned int i = 0; i < nsc->num_selections; i++) + { + if (nsc->selections[i] == nsc->num_eligible) + { + forward_dv_learn (pid, + nsc->dvl, + nsc->bi_history, + nsc->nhops, + nsc->hops, + nsc->in_time); + break; + } + } + nsc->num_eligible++; + return GNUNET_YES; +} + + +/** + * Computes the number of neighbours we should forward a DVInit + * message to given that it has so far taken @a hops_taken hops + * though the network and that the number of neighbours we have + * in total is @a neighbour_count, out of which @a eligible_count + * are not yet on the path. + * + * NOTE: technically we might want to include NSE in the formula to + * get a better grip on the overall network size. However, for now + * using NSE here would create a dependency issue in the build system. + * => Left for later, hardcoded to 50 for now. + * + * The goal of the fomula is that we want to reach a total of LOG(NSE) + * peers via DV (`target_total`). We want the reach to be spread out + * over various distances to the origin, with a bias towards shorter + * distances. + * + * We make the strong assumption that the network topology looks + * "similar" at other hops, in particular the @a neighbour_count + * should be comparable at other hops. + * + * If the local neighbourhood is densely connected, we expect that @a + * eligible_count is close to @a neighbour_count minus @a hops_taken + * as a lot of the path is already known. In that case, we should + * forward to few(er) peers to try to find a path out of the + * neighbourhood. OTOH, if @a eligible_count is close to @a + * neighbour_count, we should forward to many peers as we are either + * still close to the origin (i.e. @a hops_taken is small) or because + * we managed to get beyond a local cluster. We express this as + * the `boost_factor` using the square of the fraction of eligible + * neighbours (so if only 50% are eligible, we boost by 1/4, but if + * 99% are eligible, the 'boost' will be almost 1). + * + * Second, the more hops we have taken, the larger the problem of an + * exponential traffic explosion gets. So we take the `target_total`, + * and compute our degree such that at each distance d 2^{-d} peers + * are selected (corrected by the `boost_factor`). + * + * @param hops_taken number of hops DVInit has travelled so far + * @param neighbour_count number of neighbours we have in total + * @param eligible_count number of neighbours we could in + * theory forward to + */ +static unsigned int +calculate_fork_degree (unsigned int hops_taken, + unsigned int neighbour_count, + unsigned int eligible_count) +{ + double target_total = 50.0; /* FIXME: use LOG(NSE)? */ + double eligible_ratio = + ((double) eligible_count) / ((double) neighbour_count); + double boost_factor = eligible_ratio * eligible_ratio; + unsigned int rnd; + double left; + + if (hops_taken >= 64) + { + GNUNET_break (0); + return 0; /* precaution given bitshift below */ + } + for (unsigned int i = 1; i < hops_taken; i++) + { + /* For each hop, subtract the expected number of targets + reached at distance d (so what remains divided by 2^d) */ + target_total -= (target_total * boost_factor / (1LLU << i)); + } + rnd = + (unsigned int) floor (target_total * boost_factor / (1LLU << hops_taken)); + /* round up or down probabilistically depending on how close we were + when floor()ing to rnd */ + left = target_total - (double) rnd; + if (UINT32_MAX * left > + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX)) + rnd++; /* round up */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Forwarding DV learn message of %u hops %u(/%u/%u) times\n", + hops_taken, + rnd, + eligible_count, + neighbour_count); + return rnd; +} + + +/** + * Function called when peerstore is done storing a DV monotonic time. + * + * @param cls a `struct Neighbour` + * @param success #GNUNET_YES if peerstore was successful + */ +static void +neighbour_store_dvmono_cb (void *cls, int success) +{ + struct Neighbour *n = cls; + + n->sc = NULL; + if (GNUNET_YES != success) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to store other peer's monotonic time in peerstore!\n"); +} + + /** * Communicator gave us a DV learn message. Process the request. * @@ -5029,7 +6671,7 @@ validate_dv_initiator_signature ( * @param dvl the message that was received */ static void -handle_dv_learn (void *cls, const struct TransportDVLearn *dvl) +handle_dv_learn (void *cls, const struct TransportDVLearnMessage *dvl) { struct CommunicatorMessageContext *cmc = cls; enum GNUNET_TRANSPORT_CommunicatorCharacteristics cc; @@ -5040,8 +6682,9 @@ handle_dv_learn (void *cls, const struct TransportDVLearn *dvl) int do_fwd; int did_initiator; struct GNUNET_TIME_Absolute in_time; + struct Neighbour *n; - nhops = ntohs (dvl->bidirectional); /* 0 = sender is initiator */ + nhops = ntohs (dvl->bidirectional); /* 0 = sender is initiator */ bi_history = ntohs (dvl->bidirectional); hops = (const struct DVPathEntryP *) &dvl[1]; if (0 == nhops) @@ -5074,18 +6717,92 @@ handle_dv_learn (void *cls, const struct TransportDVLearn *dvl) /* continue communicator here, everything else can happen asynchronous! */ finish_cmc_handling (cmc); - /* OPTIMIZE-FIXME: Technically, we only need to bother checking - the initiator signature if we send the message back to the initiator... */ - if (GNUNET_OK != validate_dv_initiator_signature (&dvl->initiator, - &dvl->challenge, - &dvl->init_sig)) + n = lookup_neighbour (&dvl->initiator); + if (NULL != n) { - GNUNET_break_op (0); - return; + if ((n->dv_monotime_available == GNUNET_YES) && + (GNUNET_TIME_absolute_ntoh (dvl->monotonic_time).abs_value_us < + n->last_dv_learn_monotime.abs_value_us)) + { + GNUNET_STATISTICS_update (GST_stats, + "# DV learn discarded due to time travel", + 1, + GNUNET_NO); + return; + } + if (GNUNET_OK != validate_dv_initiator_signature (dvl->monotonic_time, + &dvl->initiator, + &dvl->challenge, + &dvl->init_sig)) + { + GNUNET_break_op (0); + return; + } + n->last_dv_learn_monotime = GNUNET_TIME_absolute_ntoh (dvl->monotonic_time); + if (GNUNET_YES == n->dv_monotime_available) + { + if (NULL != n->sc) + GNUNET_PEERSTORE_store_cancel (n->sc); + n->sc = + GNUNET_PEERSTORE_store (peerstore, + "transport", + &dvl->initiator, + GNUNET_PEERSTORE_TRANSPORT_DVLEARN_MONOTIME, + &dvl->monotonic_time, + sizeof(dvl->monotonic_time), + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + &neighbour_store_dvmono_cb, + n); + } + } + /* OPTIMIZE-FIXME: asynchronously (!) verify signatures!, + If signature verification load too high, implement random drop strategy */ + for (unsigned int i = 0; i < nhops; i++) + { + struct DvHopPS dhp = { .purpose.purpose = + htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP), + .purpose.size = htonl (sizeof(dhp)), + .pred = (0 == i) ? dvl->initiator : hops[i - 1].hop, + .succ = (nhops == i + 1) ? GST_my_identity + : hops[i + 1].hop, + .challenge = dvl->challenge }; + + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_HOP, + &dhp, + &hops[i].hop_sig, + &hops[i].hop.public_key)) + { + GNUNET_break_op (0); + return; + } + } + + if (GNUNET_EXTRA_LOGGING > 0) + { + char *path; + + path = GNUNET_strdup (GNUNET_i2s (&dvl->initiator)); + for (unsigned int i = 0; i < nhops; i++) + { + char *tmp; + + GNUNET_asprintf (&tmp, + "%s%s%s", + path, + (bi_history & (1 << (nhops - i))) ? "<->" : "-->", + GNUNET_i2s (&hops[i].hop)); + GNUNET_free (path); + path = tmp; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received DVInit via %s%s%s\n", + path, + bi_hop ? "<->" : "-->", + GNUNET_i2s (&GST_my_identity)); + GNUNET_free (path); } - // FIXME: asynchronously (!) verify hop-by-hop signatures! - // => if signature verification load too high, implement random drop - // strategy!? do_fwd = GNUNET_YES; if (0 == GNUNET_memcmp (&GST_my_identity, &dvl->initiator)) @@ -5102,7 +6819,7 @@ handle_dv_learn (void *cls, const struct TransportDVLearn *dvl) // Need also something to lookup initiation time // to compute RTT! -> add RTT argument here? - latency = GNUNET_TIME_UNIT_FOREVER_REL; // FIXME: initialize properly + latency = GNUNET_TIME_UNIT_FOREVER_REL; // FIXME: initialize properly // (based on dvl->challenge, we can identify time of origin!) network_latency = GNUNET_TIME_relative_subtract (latency, host_latency_sum); @@ -5116,6 +6833,11 @@ handle_dv_learn (void *cls, const struct TransportDVLearn *dvl) /* assumption: linear latency increase per hop */ ilat = GNUNET_TIME_relative_multiply (network_latency, i); path[i] = hops[i - 1].hop; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Learned path with %u hops to %s with latency %s\n", + i, + GNUNET_i2s (&path[i]), + GNUNET_STRINGS_relative_time_to_string (ilat, GNUNET_YES)); learn_dv_path (path, i, ilat, @@ -5126,20 +6848,20 @@ handle_dv_learn (void *cls, const struct TransportDVLearn *dvl) do_fwd = GNUNET_NO; return; } - else if (bi_hop) + if (bi_hop) { /* last hop was bi-directional, we could learn something here! */ struct GNUNET_PeerIdentity path[nhops + 2]; path[0] = GST_my_identity; - path[1] = hops[nhops - 1].hop; /* direct neighbour == predecessor! */ + path[1] = hops[nhops - 1].hop; /* direct neighbour == predecessor! */ for (unsigned int i = 0; i < nhops; i++) { int iret; if (0 == (bi_history & (1 << i))) - break; /* i-th hop not bi-directional, stop learning! */ - if (i == nhops) + break; /* i-th hop not bi-directional, stop learning! */ + if (i == nhops - 1) { path[i + 2] = dvl->initiator; } @@ -5148,6 +6870,10 @@ handle_dv_learn (void *cls, const struct TransportDVLearn *dvl) path[i + 2] = hops[nhops - i - 2].hop; } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Learned inverse path with %u hops to %s\n", + i + 1, + GNUNET_i2s (&path[i + 2])); iret = learn_dv_path (path, i + 2, GNUNET_TIME_UNIT_FOREVER_REL, @@ -5194,6 +6920,9 @@ handle_dv_learn (void *cls, const struct TransportDVLearn *dvl) GNUNET_CONTAINER_multipeermap_contains (neighbours, &dvl->initiator))) { /* send back to origin! */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending DVL back to initiator %s\n", + GNUNET_i2s (&dvl->initiator)); forward_dv_learn (&dvl->initiator, dvl, bi_history, nhops, hops, in_time); did_initiator = GNUNET_YES; } @@ -5204,17 +6933,37 @@ handle_dv_learn (void *cls, const struct TransportDVLearn *dvl) if ((do_fwd) || ((nhops < MIN_DV_PATH_LENGTH_FOR_INITIATOR) && (GNUNET_NO == did_initiator))) { - /* FIXME: loop over all neighbours, pick those with low - queues AND that are not yet on the path; possibly - adapt threshold to nhops! */ -#if FIXME - forward_dv_learn (NULL, // fill in peer from iterator here! - dvl, - bi_history, - nhops, - hops, - in_time); -#endif + /* Pick random neighbours that are not yet on the path */ + struct NeighbourSelectionContext nsc; + unsigned int n_cnt; + + n_cnt = GNUNET_CONTAINER_multipeermap_size (neighbours); + nsc.nhops = nhops; + nsc.dvl = dvl; + nsc.bi_history = bi_history; + nsc.hops = hops; + nsc.in_time = in_time; + nsc.num_eligible = 0; + GNUNET_CONTAINER_multipeermap_iterate (neighbours, + &dv_neighbour_selection, + &nsc); + if (0 == nsc.num_eligible) + return; /* done here, cannot forward to anyone else */ + nsc.num_selections = calculate_fork_degree (nhops, n_cnt, nsc.num_eligible); + nsc.num_selections = + GNUNET_MIN (MAX_DV_DISCOVERY_SELECTION, nsc.num_selections); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Forwarding DVL to %u other peers\n", + nsc.num_selections); + for (unsigned int i = 0; i < nsc.num_selections; i++) + nsc.selections[i] = + (nsc.num_selections == n_cnt) + ? i /* all were selected, avoid collisions by chance */ + : GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, n_cnt); + nsc.num_eligible = 0; + GNUNET_CONTAINER_multipeermap_iterate (neighbours, + &dv_neighbour_transmission, + &nsc); } } @@ -5227,88 +6976,276 @@ handle_dv_learn (void *cls, const struct TransportDVLearn *dvl) * @return #GNUNET_YES if message is well-formed */ static int -check_dv_box (void *cls, const struct TransportDVBox *dvb) +check_dv_box (void *cls, const struct TransportDVBoxMessage *dvb) { uint16_t size = ntohs (dvb->header.size); uint16_t num_hops = ntohs (dvb->num_hops); const struct GNUNET_PeerIdentity *hops = (const struct GNUNET_PeerIdentity *) &dvb[1]; - const struct GNUNET_MessageHeader *inbox = - (const struct GNUNET_MessageHeader *) &hops[num_hops]; - uint16_t isize; - uint16_t itype; (void) cls; - if (size < sizeof (*dvb) + num_hops * sizeof (struct GNUNET_PeerIdentity) + - sizeof (struct GNUNET_MessageHeader)) + if (size < sizeof(*dvb) + num_hops * sizeof(struct GNUNET_PeerIdentity) + + sizeof(struct GNUNET_MessageHeader)) { GNUNET_break_op (0); return GNUNET_SYSERR; } - isize = ntohs (inbox->size); - if (size != - sizeof (*dvb) + num_hops * sizeof (struct GNUNET_PeerIdentity) + isize) + /* This peer must not be on the path */ + for (unsigned int i = 0; i < num_hops; i++) + if (0 == GNUNET_memcmp (&hops[i], &GST_my_identity)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_YES; +} + + +/** + * Create a DV Box message and queue it for transmission to + * @ea next_hop. + * + * @param next_hop peer to receive the message next + * @param total_hops how many hops did the message take so far + * @param num_hops length of the @a hops array + * @param origin origin of the message + * @param hops next peer(s) to the destination, including destination + * @param payload payload of the box + * @param payload_size number of bytes in @a payload + */ +static void +forward_dv_box (struct Neighbour *next_hop, + const struct TransportDVBoxMessage *hdr, + uint16_t total_hops, + uint16_t num_hops, + const struct GNUNET_PeerIdentity *hops, + const void *enc_payload, + uint16_t enc_payload_size) +{ + struct VirtualLink *vl = next_hop->vl; + struct PendingMessage *pm; + size_t msg_size; + char *buf; + struct GNUNET_PeerIdentity *dhops; + + GNUNET_assert (NULL != vl); + msg_size = sizeof(struct TransportDVBoxMessage) + + num_hops * sizeof(struct GNUNET_PeerIdentity) + enc_payload_size; + pm = GNUNET_malloc (sizeof(struct PendingMessage) + msg_size); + pm->pmt = PMT_DV_BOX; + pm->vl = vl; + pm->timeout = GNUNET_TIME_relative_to_absolute (DV_FORWARD_TIMEOUT); + pm->logging_uuid = logging_uuid_gen++; + pm->prefs = GNUNET_MQ_PRIO_BACKGROUND; + pm->bytes_msg = msg_size; + buf = (char *) &pm[1]; + memcpy (buf, hdr, sizeof(*hdr)); + dhops = + (struct GNUNET_PeerIdentity *) &buf[sizeof(struct TransportDVBoxMessage)]; + memcpy (dhops, hops, num_hops * sizeof(struct GNUNET_PeerIdentity)); + memcpy (&dhops[num_hops], enc_payload, enc_payload_size); + GNUNET_CONTAINER_MDLL_insert (vl, + vl->pending_msg_head, + vl->pending_msg_tail, + pm); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created pending message %llu for DV Box with next hop %s (%u/%u)\n", + pm->logging_uuid, + GNUNET_i2s (&next_hop->pid), + (unsigned int) num_hops, + (unsigned int) total_hops); + check_vl_transmission (vl); +} + + +/** + * Free data structures associated with @a b. + * + * @param b data structure to release + */ +static void +free_backtalker (struct Backtalker *b) +{ + if (NULL != b->get) { - GNUNET_break_op (0); - return GNUNET_SYSERR; + GNUNET_PEERSTORE_iterate_cancel (b->get); + b->get = NULL; + GNUNET_assert (NULL != b->cmc); + finish_cmc_handling (b->cmc); + b->cmc = NULL; } - itype = ntohs (inbox->type); - if ((GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX == itype) || - (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN == itype)) + if (NULL != b->task) { - GNUNET_break_op (0); - return GNUNET_SYSERR; + GNUNET_SCHEDULER_cancel (b->task); + b->task = NULL; } - if (0 == GNUNET_memcmp (&dvb->origin, &GST_my_identity)) + if (NULL != b->sc) { - GNUNET_break_op (0); - return GNUNET_SYSERR; + GNUNET_PEERSTORE_store_cancel (b->sc); + b->sc = NULL; + } + GNUNET_assert ( + GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (backtalkers, &b->pid, b)); + GNUNET_free (b); +} + + +/** + * Callback to free backtalker records. + * + * @param cls NULL + * @param pid unused + * @param value a `struct Backtalker` + * @return #GNUNET_OK (always) + */ +static int +free_backtalker_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct Backtalker *b = value; + + (void) cls; + (void) pid; + free_backtalker (b); + return GNUNET_OK; +} + + +/** + * Function called when it is time to clean up a backtalker. + * + * @param cls a `struct Backtalker` + */ +static void +backtalker_timeout_cb (void *cls) +{ + struct Backtalker *b = cls; + + b->task = NULL; + if (0 != GNUNET_TIME_absolute_get_remaining (b->timeout).rel_value_us) + { + b->task = GNUNET_SCHEDULER_add_at (b->timeout, &backtalker_timeout_cb, b); + return; + } + GNUNET_assert (NULL == b->sc); + free_backtalker (b); +} + + +/** + * Function called with the monotonic time of a backtalker + * by PEERSTORE. Updates the time and continues processing. + * + * @param cls a `struct Backtalker` + * @param record the information found, NULL for the last call + * @param emsg error message + */ +static void +backtalker_monotime_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct Backtalker *b = cls; + struct GNUNET_TIME_AbsoluteNBO *mtbe; + struct GNUNET_TIME_Absolute mt; + + (void) emsg; + if (NULL == record) + { + /* we're done with #backtalker_monotime_cb() invocations, + continue normal processing */ + b->get = NULL; + GNUNET_assert (NULL != b->cmc); + if (0 != b->body_size) + demultiplex_with_cmc (b->cmc, + (const struct GNUNET_MessageHeader *) &b[1]); + else + finish_cmc_handling (b->cmc); + b->cmc = NULL; + return; + } + if (sizeof(*mtbe) != record->value_size) + { + GNUNET_break (0); + return; + } + mtbe = record->value; + mt = GNUNET_TIME_absolute_ntoh (*mtbe); + if (mt.abs_value_us > b->monotonic_time.abs_value_us) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Backtalker message from %s dropped, monotime in the past\n", + GNUNET_i2s (&b->pid)); + GNUNET_STATISTICS_update ( + GST_stats, + "# Backchannel messages dropped: monotonic time not increasing", + 1, + GNUNET_NO); + b->monotonic_time = mt; + /* Setting body_size to 0 prevents call to #forward_backchannel_payload() + */ + b->body_size = 0; + return; + } +} + + +/** + * Function called by PEERSTORE when the store operation of + * a backtalker's monotonic time is complete. + * + * @param cls the `struct Backtalker` + * @param success #GNUNET_OK on success + */ +static void +backtalker_monotime_store_cb (void *cls, int success) +{ + struct Backtalker *b = cls; + + if (GNUNET_OK != success) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to store backtalker's monotonic time in PEERSTORE!\n"); } - return GNUNET_YES; + b->sc = NULL; + b->task = GNUNET_SCHEDULER_add_at (b->timeout, &backtalker_timeout_cb, b); } /** - * Create a DV Box message and queue it for transmission to - * @ea next_hop. + * The backtalker @a b monotonic time changed. Update PEERSTORE. * - * @param next_hop peer to receive the message next - * @param total_hops how many hops did the message take so far - * @param num_hops length of the @a hops array - * @param origin origin of the message - * @param hops next peer(s) to the destination, including destination - * @param payload payload of the box - * @param payload_size number of bytes in @a payload + * @param b a backtalker with updated monotonic time */ static void -forward_dv_box (struct Neighbour *next_hop, - uint16_t total_hops, - uint16_t num_hops, - const struct GNUNET_PeerIdentity *origin, - const struct GNUNET_PeerIdentity *hops, - const void *payload, - uint16_t payload_size) +update_backtalker_monotime (struct Backtalker *b) { - struct TransportDVBox *dvb; - struct GNUNET_PeerIdentity *dhops; + struct GNUNET_TIME_AbsoluteNBO mtbe; - GNUNET_assert (UINT16_MAX < sizeof (struct TransportDVBox) + - sizeof (struct GNUNET_PeerIdentity) * num_hops + - payload_size); - dvb = GNUNET_malloc (sizeof (struct TransportDVBox) + - sizeof (struct GNUNET_PeerIdentity) * num_hops + - payload_size); - dvb->header.size = - htons (sizeof (struct TransportDVBox) + - sizeof (struct GNUNET_PeerIdentity) * num_hops + payload_size); - dvb->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX); - dvb->total_hops = htons (total_hops); - dvb->num_hops = htons (num_hops); - dvb->origin = *origin; - dhops = (struct GNUNET_PeerIdentity *) &dvb[1]; - memcpy (dhops, hops, num_hops * sizeof (struct GNUNET_PeerIdentity)); - memcpy (&dhops[num_hops], payload, payload_size); - route_message (&next_hop->pid, &dvb->header, RMO_NONE); + if (NULL != b->sc) + { + GNUNET_PEERSTORE_store_cancel (b->sc); + b->sc = NULL; + } + else + { + GNUNET_SCHEDULER_cancel (b->task); + b->task = NULL; + } + mtbe = GNUNET_TIME_absolute_hton (b->monotonic_time); + b->sc = + GNUNET_PEERSTORE_store (peerstore, + "transport", + &b->pid, + GNUNET_PEERSTORE_TRANSPORT_BACKCHANNEL_MONOTIME, + &mtbe, + sizeof(mtbe), + GNUNET_TIME_UNIT_FOREVER_ABS, + GNUNET_PEERSTORE_STOREOPTION_REPLACE, + &backtalker_monotime_store_cb, + b); } @@ -5320,15 +7257,39 @@ forward_dv_box (struct Neighbour *next_hop, * @param dvb the message that was received */ static void -handle_dv_box (void *cls, const struct TransportDVBox *dvb) +handle_dv_box (void *cls, const struct TransportDVBoxMessage *dvb) { struct CommunicatorMessageContext *cmc = cls; - uint16_t size = ntohs (dvb->header.size) - sizeof (*dvb); + uint16_t size = ntohs (dvb->header.size) - sizeof(*dvb); uint16_t num_hops = ntohs (dvb->num_hops); const struct GNUNET_PeerIdentity *hops = (const struct GNUNET_PeerIdentity *) &dvb[1]; - const struct GNUNET_MessageHeader *inbox = - (const struct GNUNET_MessageHeader *) &hops[num_hops]; + const char *enc_payload = (const char *) &hops[num_hops]; + uint16_t enc_payload_size = + size - (num_hops * sizeof(struct GNUNET_PeerIdentity)); + struct DVKeyState key; + struct GNUNET_HashCode hmac; + const char *hdr; + size_t hdr_len; + + if (GNUNET_EXTRA_LOGGING > 0) + { + char *path; + + path = GNUNET_strdup (GNUNET_i2s (&GST_my_identity)); + for (unsigned int i = 0; i < num_hops; i++) + { + char *tmp; + + GNUNET_asprintf (&tmp, "%s->%s", path, GNUNET_i2s (&hops[i])); + GNUNET_free (path); + path = tmp; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received DVBox with remainig path %s\n", + path); + GNUNET_free (path); + } if (num_hops > 0) { @@ -5344,16 +7305,28 @@ handle_dv_box (void *cls, const struct TransportDVBox *dvb) finish_cmc_handling (cmc); return; } - n = GNUNET_CONTAINER_multipeermap_get (neighbours, &hops[i]); + n = lookup_neighbour (&hops[i]); if (NULL == n) continue; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Skipping %u/%u hops ahead while routing DV Box\n", + i, + num_hops); forward_dv_box (n, + dvb, ntohs (dvb->total_hops) + 1, - num_hops - i - 1, /* number of hops left */ - &dvb->origin, - &hops[i + 1], /* remaining hops */ - (const void *) &dvb[1], - size); + num_hops - i - 1, /* number of hops left */ + &hops[i + 1], /* remaining hops */ + enc_payload, + enc_payload_size); + GNUNET_STATISTICS_update (GST_stats, + "# DV hops skipped routing boxes", + i, + GNUNET_NO); + GNUNET_STATISTICS_update (GST_stats, + "# DV boxes routed (total)", + 1, + GNUNET_NO); finish_cmc_handling (cmc); return; } @@ -5366,9 +7339,143 @@ handle_dv_box (void *cls, const struct TransportDVBox *dvb) return; } /* We are the target. Unbox and handle message. */ - cmc->im.sender = dvb->origin; + GNUNET_STATISTICS_update (GST_stats, + "# DV boxes opened (ultimate target)", + 1, + GNUNET_NO); cmc->total_hops = ntohs (dvb->total_hops); - demultiplex_with_cmc (cmc, inbox); + + dh_key_derive_eph_pub (&dvb->ephemeral_key, &dvb->iv, &key); + hdr = (const char *) &dvb[1]; + hdr_len = ntohs (dvb->header.size) - sizeof(*dvb); + dv_hmac (&key, &hmac, hdr, hdr_len); + if (0 != GNUNET_memcmp (&hmac, &dvb->hmac)) + { + /* HMAC missmatch, disard! */ + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + /* begin actual decryption */ + { + struct Backtalker *b; + struct GNUNET_TIME_Absolute monotime; + struct TransportDVBoxPayloadP ppay; + char body[hdr_len - sizeof(ppay)] GNUNET_ALIGN; + const struct GNUNET_MessageHeader *mh = + (const struct GNUNET_MessageHeader *) body; + + GNUNET_assert (hdr_len >= + sizeof(ppay) + sizeof(struct GNUNET_MessageHeader)); + dv_decrypt (&key, &ppay, hdr, sizeof(ppay)); + dv_decrypt (&key, &body, &hdr[sizeof(ppay)], hdr_len - sizeof(ppay)); + dv_key_clean (&key); + if (ntohs (mh->size) != sizeof(body)) + { + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + /* need to prevent box-in-a-box (and DV_LEARN) so check inbox type! */ + switch (ntohs (mh->type)) + { + case GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX: + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + + case GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN: + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + + default: + /* permitted, continue */ + break; + } + monotime = GNUNET_TIME_absolute_ntoh (ppay.monotonic_time); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Decrypted backtalk from %s\n", + GNUNET_i2s (&ppay.sender)); + b = GNUNET_CONTAINER_multipeermap_get (backtalkers, &ppay.sender); + if ((NULL != b) && (monotime.abs_value_us < b->monotonic_time.abs_value_us)) + { + GNUNET_STATISTICS_update ( + GST_stats, + "# Backchannel messages dropped: monotonic time not increasing", + 1, + GNUNET_NO); + finish_cmc_handling (cmc); + return; + } + if ((NULL == b) || + (0 != GNUNET_memcmp (&b->last_ephemeral, &dvb->ephemeral_key))) + { + /* Check signature */ + struct EphemeralConfirmationPS ec; + + ec.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL); + ec.purpose.size = htonl (sizeof(ec)); + ec.target = GST_my_identity; + ec.ephemeral_key = dvb->ephemeral_key; + if ( + GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify ( + GNUNET_SIGNATURE_PURPOSE_TRANSPORT_EPHEMERAL, + &ec, + &ppay.sender_sig, + &ppay.sender.public_key)) + { + /* Signature invalid, disard! */ + GNUNET_break_op (0); + finish_cmc_handling (cmc); + return; + } + } + /* Update sender, we now know the real origin! */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "DVBox received for me from %s via %s\n", + GNUNET_i2s2 (&ppay.sender), + GNUNET_i2s (&cmc->im.sender)); + cmc->im.sender = ppay.sender; + + if (NULL != b) + { + /* update key cache and mono time */ + b->last_ephemeral = dvb->ephemeral_key; + b->monotonic_time = monotime; + update_backtalker_monotime (b); + b->timeout = + GNUNET_TIME_relative_to_absolute (BACKCHANNEL_INACTIVITY_TIMEOUT); + + demultiplex_with_cmc (cmc, mh); + return; + } + /* setup data structure to cache signature AND check + monotonic time with PEERSTORE before forwarding backchannel payload */ + b = GNUNET_malloc (sizeof(struct Backtalker) + sizeof(body)); + b->pid = ppay.sender; + b->body_size = sizeof(body); + memcpy (&b[1], body, sizeof(body)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put ( + backtalkers, + &b->pid, + b, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + b->monotonic_time = monotime; /* NOTE: to be checked still! */ + b->cmc = cmc; + b->timeout = + GNUNET_TIME_relative_to_absolute (BACKCHANNEL_INACTIVITY_TIMEOUT); + b->task = GNUNET_SCHEDULER_add_at (b->timeout, &backtalker_timeout_cb, b); + b->get = + GNUNET_PEERSTORE_iterate (peerstore, + "transport", + &b->pid, + GNUNET_PEERSTORE_TRANSPORT_BACKCHANNEL_MONOTIME, + &backtalker_monotime_cb, + b); + } /* end actual decryption */ } @@ -5395,6 +7502,187 @@ check_incoming_msg (void *cls, } +/** + * Closure for #check_known_address. + */ +struct CheckKnownAddressContext +{ + /** + * Set to the address we are looking for. + */ + const char *address; + + /** + * Set to a matching validation state, if one was found. + */ + struct ValidationState *vs; +}; + + +/** + * Test if the validation state in @a value matches the + * address from @a cls. + * + * @param cls a `struct CheckKnownAddressContext` + * @param pid unused (must match though) + * @param value a `struct ValidationState` + * @return #GNUNET_OK if not matching, #GNUNET_NO if match found + */ +static int +check_known_address (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) +{ + struct CheckKnownAddressContext *ckac = cls; + struct ValidationState *vs = value; + + (void) pid; + if (0 != strcmp (vs->address, ckac->address)) + return GNUNET_OK; + ckac->vs = vs; + return GNUNET_NO; +} + + +/** + * Task run periodically to validate some address based on #validation_heap. + * + * @param cls NULL + */ +static void +validation_start_cb (void *cls); + + +/** + * Set the time for next_challenge of @a vs to @a new_time. + * Updates the heap and if necessary reschedules the job. + * + * @param vs validation state to update + * @param new_time new time for revalidation + */ +static void +update_next_challenge_time (struct ValidationState *vs, + struct GNUNET_TIME_Absolute new_time) +{ + struct GNUNET_TIME_Relative delta; + + if (new_time.abs_value_us == vs->next_challenge.abs_value_us) + return; /* be lazy */ + vs->next_challenge = new_time; + if (NULL == vs->hn) + vs->hn = + GNUNET_CONTAINER_heap_insert (validation_heap, vs, new_time.abs_value_us); + else + GNUNET_CONTAINER_heap_update_cost (vs->hn, new_time.abs_value_us); + if ((vs != GNUNET_CONTAINER_heap_peek (validation_heap)) && + (NULL != validation_task)) + return; + if (NULL != validation_task) + GNUNET_SCHEDULER_cancel (validation_task); + /* randomize a bit */ + delta.rel_value_us = + GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + MIN_DELAY_ADDRESS_VALIDATION.rel_value_us); + new_time = GNUNET_TIME_absolute_add (new_time, delta); + validation_task = + GNUNET_SCHEDULER_add_at (new_time, &validation_start_cb, NULL); +} + + +/** + * Start address validation. + * + * @param pid peer the @a address is for + * @param address an address to reach @a pid (presumably) + */ +static void +start_address_validation (const struct GNUNET_PeerIdentity *pid, + const char *address) +{ + struct GNUNET_TIME_Absolute now; + struct ValidationState *vs; + struct CheckKnownAddressContext ckac = { .address = address, .vs = NULL }; + + (void) GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, + pid, + &check_known_address, + &ckac); + if (NULL != (vs = ckac.vs)) + { + /* if 'vs' is not currently valid, we need to speed up retrying the + * validation */ + if (vs->validated_until.abs_value_us < vs->next_challenge.abs_value_us) + { + /* reduce backoff as we got a fresh advertisement */ + vs->challenge_backoff = + GNUNET_TIME_relative_min (FAST_VALIDATION_CHALLENGE_FREQ, + GNUNET_TIME_relative_divide ( + vs->challenge_backoff, + 2)); + update_next_challenge_time (vs, + GNUNET_TIME_relative_to_absolute ( + vs->challenge_backoff)); + } + return; + } + now = GNUNET_TIME_absolute_get (); + vs = GNUNET_new (struct ValidationState); + vs->pid = *pid; + vs->valid_until = + GNUNET_TIME_relative_to_absolute (ADDRESS_VALIDATION_LIFETIME); + vs->first_challenge_use = now; + vs->validation_rtt = GNUNET_TIME_UNIT_FOREVER_REL; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &vs->challenge, + sizeof(vs->challenge)); + vs->address = GNUNET_strdup (address); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting address validation `%s' of peer %s using challenge %s\n", + address, + GNUNET_i2s (pid), + GNUNET_sh2s (&vs->challenge.value)); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put ( + validation_map, + &vs->pid, + vs, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + update_next_challenge_time (vs, now); +} + + +/** + * Function called by PEERSTORE for each matching record. + * + * @param cls closure, a `struct IncomingRequest` + * @param record peerstore record information + * @param emsg error message, or NULL if no errors + */ +static void +handle_hello_for_incoming (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct IncomingRequest *ir = cls; + const char *val; + + if (NULL != emsg) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Got failure from PEERSTORE: %s\n", + emsg); + return; + } + val = record->value; + if ((0 == record->value_size) || ('\0' != val[record->value_size - 1])) + { + GNUNET_break (0); + return; + } + start_address_validation (&ir->pid, (const char *) record->value); +} + + /** * Communicator gave us a transport address validation challenge. Process the * request. @@ -5404,42 +7692,90 @@ check_incoming_msg (void *cls, * @param tvc the message that was received */ static void -handle_validation_challenge (void *cls, - const struct TransportValidationChallenge *tvc) +handle_validation_challenge ( + void *cls, + const struct TransportValidationChallengeMessage *tvc) { struct CommunicatorMessageContext *cmc = cls; - struct TransportValidationResponse *tvr; + struct TransportValidationResponseMessage tvr; + struct VirtualLink *vl; + struct GNUNET_TIME_RelativeNBO validity_duration; + struct IncomingRequest *ir; + struct Neighbour *n; + struct GNUNET_PeerIdentity sender; + /* DV-routed messages are not allowed for validation challenges */ if (cmc->total_hops > 0) { - /* DV routing is not allowed for validation challenges! */ GNUNET_break_op (0); finish_cmc_handling (cmc); return; } - tvr = GNUNET_new (struct TransportValidationResponse); - tvr->header.type = + validity_duration = cmc->im.expected_address_validity; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received address validation challenge %s\n", + GNUNET_sh2s (&tvc->challenge.value)); + /* If we have a virtual link, we use this mechanism to signal the + size of the flow control window, and to allow the sender + to ask for increases. If for us the virtual link is still down, + we will always give a window size of zero. */ + tvr.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_RESPONSE); - tvr->header.size = htons (sizeof (*tvr)); - tvr->challenge = tvc->challenge; - tvr->origin_time = tvc->sender_time; - tvr->validity_duration = cmc->im.expected_address_validity; + tvr.header.size = htons (sizeof(tvr)); + tvr.reserved = htonl (0); + tvr.challenge = tvc->challenge; + tvr.origin_time = tvc->sender_time; + tvr.validity_duration = validity_duration; { /* create signature */ - struct TransportValidationPS tvp = - {.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE), - .purpose.size = htonl (sizeof (tvp)), - .validity_duration = tvr->validity_duration, - .challenge = tvc->challenge}; - - GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, - &tvp.purpose, - &tvr->signature)); - } - route_message (&cmc->im.sender, - &tvr->header, - RMO_ANYTHING_GOES | RMO_REDUNDANT); + struct TransportValidationPS tvp = { + .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE), + .purpose.size = htonl (sizeof(tvp)), + .validity_duration = validity_duration, + .challenge = tvc->challenge + }; + + GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, + &tvp, + &tvr.signature); + } + route_control_message_without_fc (&cmc->im.sender, + &tvr.header, + RMO_ANYTHING_GOES | RMO_REDUNDANT); + sender = cmc->im.sender; finish_cmc_handling (cmc); + vl = lookup_virtual_link (&sender); + if (NULL != vl) + return; + + /* For us, the link is still down, but we need bi-directional + connections (for flow-control and for this to be useful for + CORE), so we must try to bring the link up! */ + + /* (1) Check existing queues, if any, we may be lucky! */ + n = lookup_neighbour (&sender); + if (NULL != n) + for (struct Queue *q = n->queue_head; NULL != q; q = q->next_neighbour) + start_address_validation (&sender, q->address); + /* (2) Also try to see if we have addresses in PEERSTORE for this peer + we could use */ + for (ir = ir_head; NULL != ir; ir = ir->next) + if (0 == GNUNET_memcmp (&ir->pid, &sender)) + return; + /* we are already trying */ + ir = GNUNET_new (struct IncomingRequest); + ir->pid = sender; + GNUNET_CONTAINER_DLL_insert (ir_head, ir_tail, ir); + ir->wc = GNUNET_PEERSTORE_watch (peerstore, + "transport", + &ir->pid, + GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY, + &handle_hello_for_incoming, + ir); + ir_total++; + /* Bound attempts we do in parallel here, might otherwise get excessive */ + while (ir_total > MAX_INCOMING_REQUEST) + free_incoming_request (ir_head); } @@ -5451,7 +7787,7 @@ struct CheckKnownChallengeContext /** * Set to the challenge we are looking for. */ - const struct GNUNET_ShortHashCode *challenge; + const struct ChallengeNonceP *challenge; /** * Set to a matching validation state, if one was found. @@ -5507,51 +7843,6 @@ peerstore_store_validation_cb (void *cls, int success) } -/** - * Task run periodically to validate some address based on #validation_heap. - * - * @param cls NULL - */ -static void -validation_start_cb (void *cls); - - -/** - * Set the time for next_challenge of @a vs to @a new_time. - * Updates the heap and if necessary reschedules the job. - * - * @param vs validation state to update - * @param new_time new time for revalidation - */ -static void -update_next_challenge_time (struct ValidationState *vs, - struct GNUNET_TIME_Absolute new_time) -{ - struct GNUNET_TIME_Relative delta; - - if (new_time.abs_value_us == vs->next_challenge.abs_value_us) - return; /* be lazy */ - vs->next_challenge = new_time; - if (NULL == vs->hn) - vs->hn = - GNUNET_CONTAINER_heap_insert (validation_heap, vs, new_time.abs_value_us); - else - GNUNET_CONTAINER_heap_update_cost (vs->hn, new_time.abs_value_us); - if ((vs != GNUNET_CONTAINER_heap_peek (validation_heap)) && - (NULL != validation_task)) - return; - if (NULL != validation_task) - GNUNET_SCHEDULER_cancel (validation_task); - /* randomize a bit */ - delta.rel_value_us = - GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, - MIN_DELAY_ADDRESS_VALIDATION.rel_value_us); - new_time = GNUNET_TIME_absolute_add (new_time, delta); - validation_task = - GNUNET_SCHEDULER_add_at (new_time, &validation_start_cb, NULL); -} - - /** * Find the queue matching @a pid and @a address. * @@ -5564,7 +7855,7 @@ find_queue (const struct GNUNET_PeerIdentity *pid, const char *address) { struct Neighbour *n; - n = GNUNET_CONTAINER_multipeermap_get (neighbours, pid); + n = lookup_neighbour (pid); if (NULL == n) return NULL; for (struct Queue *pos = n->queue_head; NULL != pos; @@ -5577,75 +7868,6 @@ find_queue (const struct GNUNET_PeerIdentity *pid, const char *address) } -/** - * Task run periodically to check whether the validity of the given queue has - * run its course. If so, finds either another queue to take over, or clears - * the neighbour's `core_visible` flag. In the latter case, gives DV routes a - * chance to take over, and if that fails, notifies CORE about the disconnect. - * - * @param cls a `struct Queue` - */ -static void -core_queue_visibility_check (void *cls) -{ - struct Queue *q = cls; - - q->visibility_task = NULL; - if (0 != GNUNET_TIME_absolute_get_remaining (q->validated_until).rel_value_us) - { - q->visibility_task = GNUNET_SCHEDULER_add_at (q->validated_until, - &core_queue_visibility_check, - q); - return; - } - update_neighbour_core_visibility (q->neighbour); -} - - -/** - * Check whether the CORE visibility of @a n should change. Finds either a - * queue to preserve the visibility, or clears the neighbour's `core_visible` - * flag. In the latter case, gives DV routes a chance to take over, and if - * that fails, notifies CORE about the disconnect. If so, check whether we - * need to notify CORE. - * - * @param n neighbour to perform the check for - */ -static void -update_neighbour_core_visibility (struct Neighbour *n) -{ - struct DistanceVector *dv; - - GNUNET_assert (GNUNET_YES == n->core_visible); - /* Check if _any_ queue of this neighbour is still valid, if so, schedule - the #core_queue_visibility_check() task for that queue */ - for (struct Queue *q = n->queue_head; NULL != q; q = q->next_neighbour) - { - if (0 != - GNUNET_TIME_absolute_get_remaining (q->validated_until).rel_value_us) - { - /* found a valid queue, use this one */ - q->visibility_task = - GNUNET_SCHEDULER_add_at (q->validated_until, - &core_queue_visibility_check, - q); - return; - } - } - n->core_visible = GNUNET_NO; - - /* Check if _any_ DV route to this neighbour is currently - valid, if so, do NOT tell core about the loss of direct - connectivity (DV still counts!) */ - dv = GNUNET_CONTAINER_multipeermap_get (dv_routes, &n->pid); - if (GNUNET_YES == dv->core_visible) - return; - /* Nothing works anymore, need to tell CORE about the loss of - connectivity! */ - cores_send_disconnect_info (&n->pid); -} - - /** * Communicator gave us a transport address validation response. Process the * request. @@ -5655,17 +7877,18 @@ update_neighbour_core_visibility (struct Neighbour *n) * @param tvr the message that was received */ static void -handle_validation_response (void *cls, - const struct TransportValidationResponse *tvr) +handle_validation_response ( + void *cls, + const struct TransportValidationResponseMessage *tvr) { struct CommunicatorMessageContext *cmc = cls; struct ValidationState *vs; - struct CheckKnownChallengeContext ckac = {.challenge = &tvr->challenge, - .vs = NULL}; + struct CheckKnownChallengeContext ckac = { .challenge = &tvr->challenge, + .vs = NULL }; struct GNUNET_TIME_Absolute origin_time; struct Queue *q; - struct DistanceVector *dv; struct Neighbour *n; + struct VirtualLink *vl; /* check this is one of our challenges */ (void) GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, @@ -5680,6 +7903,9 @@ handle_validation_response (void *cls, "# Validations dropped, challenge unknown", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Validation response %s dropped, challenge unknown\n", + GNUNET_sh2s (&tvr->challenge.value)); finish_cmc_handling (cmc); return; } @@ -5696,16 +7922,17 @@ handle_validation_response (void *cls, { /* check signature */ - struct TransportValidationPS tvp = - {.purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE), - .purpose.size = htonl (sizeof (tvp)), - .validity_duration = tvr->validity_duration, - .challenge = tvr->challenge}; + struct TransportValidationPS tvp = { + .purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE), + .purpose.size = htonl (sizeof(tvp)), + .validity_duration = tvr->validity_duration, + .challenge = tvr->challenge + }; if ( GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_CHALLENGE, - &tvp.purpose, + &tvp, &tvr->signature, &cmc->im.sender.public_key)) { @@ -5729,7 +7956,7 @@ handle_validation_response (void *cls, vs->challenge_backoff = GNUNET_TIME_UNIT_ZERO; GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &vs->challenge, - sizeof (vs->challenge)); + sizeof(vs->challenge)); vs->first_challenge_use = GNUNET_TIME_absolute_subtract ( vs->validated_until, GNUNET_TIME_relative_multiply (vs->validation_rtt, @@ -5737,6 +7964,10 @@ handle_validation_response (void *cls, vs->last_challenge_use = GNUNET_TIME_UNIT_ZERO_ABS; /* challenge was not yet used */ update_next_challenge_time (vs, vs->first_challenge_use); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Validation response %s accepted, address valid until %s\n", + GNUNET_sh2s (&tvr->challenge.value), + GNUNET_STRINGS_absolute_time_to_string (vs->valid_until)); vs->sc = GNUNET_PEERSTORE_store (peerstore, "transport", &cmc->im.sender, @@ -5761,26 +7992,47 @@ handle_validation_response (void *cls, return; } q->validated_until = vs->validated_until; - q->rtt = vs->validation_rtt; + q->pd.aged_rtt = vs->validation_rtt; n = q->neighbour; - if (GNUNET_NO != n->core_visible) - return; /* nothing changed, we are done here */ - n->core_visible = GNUNET_YES; - q->visibility_task = GNUNET_SCHEDULER_add_at (q->validated_until, - &core_queue_visibility_check, - q); - /* Check if _any_ DV route to this neighbour is - currently valid, if so, do NOT tell core anything! */ - dv = GNUNET_CONTAINER_multipeermap_get (dv_routes, &n->pid); - if ((NULL != dv) && (GNUNET_YES == dv->core_visible)) - return; /* nothing changed, done */ - /* We lacked a confirmed connection to the neighbour + vl = lookup_virtual_link (&vs->pid); + if (NULL != vl) + { + /* Link was already up, remember n is also now available and we are done */ + if (NULL == vl->n) + { + vl->n = n; + n->vl = vl; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Virtual link to %s could now also direct neighbour!\n", + GNUNET_i2s (&vs->pid)); + } + else + { + GNUNET_assert (n == vl->n); + } + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating new virtual link to %s using direct neighbour!\n", + GNUNET_i2s (&vs->pid)); + vl = GNUNET_new (struct VirtualLink); + vl->target = n->pid; + vl->n = n; + n->vl = vl; + vl->core_recv_window = RECV_WINDOW_SIZE; + vl->available_fc_window_size = DEFAULT_WINDOW_SIZE; + vl->visibility_task = + GNUNET_SCHEDULER_add_at (q->validated_until, &check_link_down, vl); + GNUNET_break (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put ( + links, + &vl->target, + vl, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + consider_sending_fc (vl); + /* We lacked a confirmed connection to the target before, so tell CORE about it (finally!) */ - cores_send_connect_info (&n->pid, - (NULL != dv) - ? GNUNET_BANDWIDTH_value_sum (dv->quota_out, - n->quota_out) - : n->quota_out); + cores_send_connect_info (&n->pid); } @@ -5799,10 +8051,98 @@ handle_incoming_msg (void *cls, cmc->tc = tc; cmc->im = *im; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received message via communicator from peer %s\n", + GNUNET_i2s (&im->sender)); demultiplex_with_cmc (cmc, (const struct GNUNET_MessageHeader *) &im[1]); } +/** + * Communicator gave us a transport address validation response. Process the + * request. + * + * @param cls a `struct CommunicatorMessageContext` (must call + * #finish_cmc_handling() when done) + * @param fc the message that was received + */ +static void +handle_flow_control (void *cls, const struct TransportFlowControlMessage *fc) +{ + struct CommunicatorMessageContext *cmc = cls; + struct VirtualLink *vl; + uint32_t seq; + struct GNUNET_TIME_Absolute st; + uint64_t os; + uint64_t wnd; + + vl = lookup_virtual_link (&cmc->im.sender); + if (NULL == vl) + { + GNUNET_STATISTICS_update (GST_stats, + "# FC dropped: virtual link unknown", + 1, + GNUNET_NO); + finish_cmc_handling (cmc); + return; + } + st = GNUNET_TIME_absolute_ntoh (fc->sender_time); + if (st.abs_value_us < vl->last_fc_timestamp.abs_value_us) + { + /* out of order, drop */ + GNUNET_STATISTICS_update (GST_stats, + "# FC dropped: message out of order", + 1, + GNUNET_NO); + finish_cmc_handling (cmc); + return; + } + seq = ntohl (fc->seq); + if (seq < vl->last_fc_seq) + { + /* Wrap-around/reset of other peer; start all counters from zero */ + vl->outbound_fc_window_size_used = 0; + } + vl->last_fc_seq = seq; + vl->last_fc_timestamp = st; + vl->outbound_fc_window_size = GNUNET_ntohll (fc->inbound_window_size); + os = GNUNET_ntohll (fc->outbound_sent); + vl->incoming_fc_window_size_loss = + (int64_t) (os - vl->incoming_fc_window_size_used); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received FC from %s, seq %u, new window %llu (loss at %lld)\n", + GNUNET_i2s (&vl->target), + (unsigned int) seq, + (unsigned long long) vl->outbound_fc_window_size, + (long long) vl->incoming_fc_window_size_loss); + wnd = GNUNET_ntohll (fc->outbound_window_size); + if ((wnd < vl->incoming_fc_window_size) || + (vl->last_outbound_window_size_received != wnd) || + (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, UINT32_MAX) + % FC_NO_CHANGE_REPLY_PROBABILITY)) + { + /* Consider re-sending our FC message, as clearly the + other peer's idea of the window is not up-to-date */ + consider_sending_fc (vl); + } + if ((wnd == vl->incoming_fc_window_size) && + (vl->last_outbound_window_size_received == wnd) && + (NULL != vl->fc_retransmit_task)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Stopping FC retransmission to %s: peer is current at window %llu\n", + GNUNET_i2s (&vl->target), + (unsigned long long) wnd); + GNUNET_SCHEDULER_cancel (vl->fc_retransmit_task); + vl->fc_retransmit_task = NULL; + } + vl->last_outbound_window_size_received = wnd; + /* FC window likely increased, check transmission possibilities! */ + check_vl_transmission (vl); + finish_cmc_handling (cmc); +} + + /** * Given an inbound message @a msg from a communicator @a cmc, * demultiplex it based on the type calling the right handler. @@ -5815,47 +8155,51 @@ demultiplex_with_cmc (struct CommunicatorMessageContext *cmc, const struct GNUNET_MessageHeader *msg) { struct GNUNET_MQ_MessageHandler handlers[] = - {GNUNET_MQ_hd_var_size (fragment_box, - GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT, - struct TransportFragmentBox, - &cmc), - GNUNET_MQ_hd_fixed_size (fragment_ack, - GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT_ACK, - struct TransportFragmentAckMessage, - &cmc), - GNUNET_MQ_hd_var_size (reliability_box, - GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_BOX, - struct TransportReliabilityBox, - &cmc), - GNUNET_MQ_hd_fixed_size (reliability_ack, - GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_ACK, - struct TransportReliabilityAckMessage, - &cmc), - GNUNET_MQ_hd_var_size (backchannel_encapsulation, - GNUNET_MESSAGE_TYPE_TRANSPORT_BACKCHANNEL_ENCAPSULATION, - struct TransportBackchannelEncapsulationMessage, - &cmc), - GNUNET_MQ_hd_var_size (dv_learn, - GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN, - struct TransportDVLearn, - &cmc), - GNUNET_MQ_hd_var_size (dv_box, - GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX, - struct TransportDVBox, - &cmc), - GNUNET_MQ_hd_fixed_size ( - validation_challenge, - GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_CHALLENGE, - struct TransportValidationChallenge, - &cmc), - GNUNET_MQ_hd_fixed_size ( - validation_response, - GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_RESPONSE, - struct TransportValidationResponse, - &cmc), - GNUNET_MQ_handler_end ()}; + { GNUNET_MQ_hd_var_size (fragment_box, + GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT, + struct TransportFragmentBoxMessage, + &cmc), + GNUNET_MQ_hd_var_size (reliability_box, + GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_BOX, + struct TransportReliabilityBoxMessage, + &cmc), + GNUNET_MQ_hd_var_size (reliability_ack, + GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_ACK, + struct TransportReliabilityAckMessage, + &cmc), + GNUNET_MQ_hd_var_size (backchannel_encapsulation, + GNUNET_MESSAGE_TYPE_TRANSPORT_BACKCHANNEL_ENCAPSULATION, + struct TransportBackchannelEncapsulationMessage, + &cmc), + GNUNET_MQ_hd_var_size (dv_learn, + GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN, + struct TransportDVLearnMessage, + &cmc), + GNUNET_MQ_hd_var_size (dv_box, + GNUNET_MESSAGE_TYPE_TRANSPORT_DV_BOX, + struct TransportDVBoxMessage, + &cmc), + GNUNET_MQ_hd_fixed_size ( + validation_challenge, + GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_CHALLENGE, + struct TransportValidationChallengeMessage, + &cmc), + GNUNET_MQ_hd_fixed_size (flow_control, + GNUNET_MESSAGE_TYPE_TRANSPORT_FLOW_CONTROL, + struct TransportFlowControlMessage, + &cmc), + GNUNET_MQ_hd_fixed_size ( + validation_response, + GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_RESPONSE, + struct TransportValidationResponseMessage, + &cmc), + GNUNET_MQ_handler_end () }; int ret; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Handling message of type %u with %u bytes\n", + (unsigned int) ntohs (msg->type), + (unsigned int) ntohs (msg->size)); ret = GNUNET_MQ_handle_message (handlers, msg); if (GNUNET_SYSERR == ret) { @@ -5895,38 +8239,61 @@ check_add_queue_message (void *cls, /** - * Bandwidth tracker informs us that the delay until we should receive - * more has changed. + * If necessary, generates the UUID for a @a pm * - * @param cls a `struct Queue` for which the delay changed + * @param pm pending message to generate UUID for. */ static void -tracker_update_in_cb (void *cls) +set_pending_message_uuid (struct PendingMessage *pm) { - struct Queue *queue = cls; - struct GNUNET_TIME_Relative in_delay; - unsigned int rsize; - - rsize = (0 == queue->mtu) ? IN_PACKET_SIZE_WITHOUT_MTU : queue->mtu; - in_delay = GNUNET_BANDWIDTH_tracker_get_delay (&queue->tracker_in, rsize); - // FIXME: how exactly do we do inbound flow control? + if (pm->msg_uuid_set) + return; + pm->msg_uuid.uuid = pm->vl->message_uuid_ctr++; + pm->msg_uuid_set = GNUNET_YES; } /** - * If necessary, generates the UUID for a @a pm + * Setup data structure waiting for acknowledgements. * - * @param pm pending message to generate UUID for. + * @param queue queue the @a pm will be sent over + * @param dvh path the message will take, may be NULL + * @param pm the pending message for transmission + * @return corresponding fresh pending acknowledgement */ -static void -set_pending_message_uuid (struct PendingMessage *pm) +static struct PendingAcknowledgement * +prepare_pending_acknowledgement (struct Queue *queue, + struct DistanceVectorHop *dvh, + struct PendingMessage *pm) { - if (pm->msg_uuid_set) - return; - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &pm->msg_uuid, - sizeof (pm->msg_uuid)); - pm->msg_uuid_set = GNUNET_YES; + struct PendingAcknowledgement *pa; + + pa = GNUNET_new (struct PendingAcknowledgement); + pa->queue = queue; + pa->dvh = dvh; + pa->pm = pm; + do + { + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &pa->ack_uuid, + sizeof(pa->ack_uuid)); + } + while (GNUNET_YES != GNUNET_CONTAINER_multiuuidmap_put ( + pending_acks, + &pa->ack_uuid.value, + pa, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + GNUNET_CONTAINER_MDLL_insert (queue, queue->pa_head, queue->pa_tail, pa); + GNUNET_CONTAINER_MDLL_insert (pm, pm->pa_head, pm->pa_tail, pa); + if (NULL != dvh) + GNUNET_CONTAINER_MDLL_insert (dvh, dvh->pa_head, dvh->pa_tail, pa); + pa->transmission_time = GNUNET_TIME_absolute_get (); + pa->message_size = pm->bytes_msg; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Waiting for ACKnowledgment `%s' for <%llu>\n", + GNUNET_uuid2s (&pa->ack_uuid.value), + pm->logging_uuid); + return pa; } @@ -5936,35 +8303,51 @@ set_pending_message_uuid (struct PendingMessage *pm) * @a mtu is too small, generates and error for the @a pm * and returns NULL. * + * @param queue which queue to fragment for + * @param dvh path the message will take, or NULL * @param pm pending message to fragment for transmission - * @param mtu MTU to apply * @return new message to transmit */ static struct PendingMessage * -fragment_message (struct PendingMessage *pm, uint16_t mtu) +fragment_message (struct Queue *queue, + struct DistanceVectorHop *dvh, + struct PendingMessage *pm) { + struct PendingAcknowledgement *pa; struct PendingMessage *ff; + uint16_t mtu; + mtu = (0 == queue->mtu) + ? UINT16_MAX - sizeof(struct GNUNET_TRANSPORT_SendMessageTo) + : queue->mtu; set_pending_message_uuid (pm); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Fragmenting message %llu <%llu> to %s for MTU %u\n", + (unsigned long long) pm->msg_uuid.uuid, + pm->logging_uuid, + GNUNET_i2s (&pm->vl->target), + (unsigned int) mtu); + pa = prepare_pending_acknowledgement (queue, dvh, pm); /* This invariant is established in #handle_add_queue_message() */ - GNUNET_assert (mtu > sizeof (struct TransportFragmentBox)); + GNUNET_assert (mtu > sizeof(struct TransportFragmentBoxMessage)); /* select fragment for transmission, descending the tree if it has - been expanded until we are at a leaf or at a fragment that is small enough + been expanded until we are at a leaf or at a fragment that is small + enough */ ff = pm; while (((ff->bytes_msg > mtu) || (pm == ff)) && (ff->frag_off == ff->bytes_msg) && (NULL != ff->head_frag)) { - ff = ff->head_frag; /* descent into fragmented fragments */ + ff = ff->head_frag; /* descent into fragmented fragments */ } if (((ff->bytes_msg > mtu) || (pm == ff)) && (pm->frag_off < pm->bytes_msg)) { /* Did not yet calculate all fragments, calculate next fragment */ struct PendingMessage *frag; - struct TransportFragmentBox tfb; + struct TransportFragmentBoxMessage tfb; const char *orig; char *msg; uint16_t fragmax; @@ -5976,31 +8359,34 @@ fragment_message (struct PendingMessage *pm, uint16_t mtu) msize = ff->bytes_msg; if (pm != ff) { - const struct TransportFragmentBox *tfbo; + const struct TransportFragmentBoxMessage *tfbo; - tfbo = (const struct TransportFragmentBox *) orig; - orig += sizeof (struct TransportFragmentBox); - msize -= sizeof (struct TransportFragmentBox); + tfbo = (const struct TransportFragmentBoxMessage *) orig; + orig += sizeof(struct TransportFragmentBoxMessage); + msize -= sizeof(struct TransportFragmentBoxMessage); xoff = ntohs (tfbo->frag_off); } - fragmax = mtu - sizeof (struct TransportFragmentBox); + fragmax = mtu - sizeof(struct TransportFragmentBoxMessage); fragsize = GNUNET_MIN (msize - ff->frag_off, fragmax); - frag = GNUNET_malloc (sizeof (struct PendingMessage) + - sizeof (struct TransportFragmentBox) + fragsize); - frag->target = pm->target; + frag = + GNUNET_malloc (sizeof(struct PendingMessage) + + sizeof(struct TransportFragmentBoxMessage) + fragsize); + frag->logging_uuid = logging_uuid_gen++; + frag->vl = pm->vl; frag->frag_parent = ff; frag->timeout = pm->timeout; - frag->bytes_msg = sizeof (struct TransportFragmentBox) + fragsize; + frag->bytes_msg = sizeof(struct TransportFragmentBoxMessage) + fragsize; frag->pmt = PMT_FRAGMENT_BOX; msg = (char *) &frag[1]; tfb.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_FRAGMENT); - tfb.header.size = htons (sizeof (struct TransportFragmentBox) + fragsize); - tfb.frag_uuid = htonl (pm->frag_uuidgen++); + tfb.header.size = + htons (sizeof(struct TransportFragmentBoxMessage) + fragsize); + tfb.ack_uuid = pa->ack_uuid; tfb.msg_uuid = pm->msg_uuid; tfb.frag_off = htons (ff->frag_off + xoff); tfb.msg_size = htons (pm->bytes_msg); - memcpy (msg, &tfb, sizeof (tfb)); - memcpy (&msg[sizeof (tfb)], &orig[ff->frag_off], fragsize); + memcpy (msg, &tfb, sizeof(tfb)); + memcpy (&msg[sizeof(tfb)], &orig[ff->frag_off], fragsize); GNUNET_CONTAINER_MDLL_insert (frag, ff->head_frag, ff->tail_frag, frag); ff->frag_off += fragsize; ff = frag; @@ -6026,13 +8412,18 @@ fragment_message (struct PendingMessage *pm, uint16_t mtu) * @a pm). If the @a pm is already fragmented or reliability boxed, * or itself an ACK, this function simply returns @a pm. * + * @param queue which queue to prepare transmission for + * @param dvh path the message will take, or NULL * @param pm pending message to box for transmission over unreliabile queue * @return new message to transmit */ static struct PendingMessage * -reliability_box_message (struct PendingMessage *pm) +reliability_box_message (struct Queue *queue, + struct DistanceVectorHop *dvh, + struct PendingMessage *pm) { - struct TransportReliabilityBox rbox; + struct TransportReliabilityBoxMessage rbox; + struct PendingAcknowledgement *pa; struct PendingMessage *bpm; char *msg; @@ -6042,29 +8433,38 @@ reliability_box_message (struct PendingMessage *pm) if (NULL != pm->bpm) return pm->bpm; /* already computed earlier: do nothing */ GNUNET_assert (NULL == pm->head_frag); - if (pm->bytes_msg + sizeof (rbox) > UINT16_MAX) + if (pm->bytes_msg + sizeof(rbox) > UINT16_MAX) { /* failed hard */ GNUNET_break (0); - client_send_response (pm, GNUNET_NO, 0); + client_send_response (pm); return NULL; } - bpm = GNUNET_malloc (sizeof (struct PendingMessage) + sizeof (rbox) + - pm->bytes_msg); - bpm->target = pm->target; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Preparing reliability box for message <%llu> to %s on queue %s\n", + pm->logging_uuid, + GNUNET_i2s (&pm->vl->target), + queue->address); + pa = prepare_pending_acknowledgement (queue, dvh, pm); + + bpm = GNUNET_malloc (sizeof(struct PendingMessage) + sizeof(rbox) + + pm->bytes_msg); + bpm->logging_uuid = logging_uuid_gen++; + bpm->vl = pm->vl; bpm->frag_parent = pm; GNUNET_CONTAINER_MDLL_insert (frag, pm->head_frag, pm->tail_frag, bpm); bpm->timeout = pm->timeout; bpm->pmt = PMT_RELIABILITY_BOX; - bpm->bytes_msg = pm->bytes_msg + sizeof (rbox); + bpm->bytes_msg = pm->bytes_msg + sizeof(rbox); set_pending_message_uuid (bpm); rbox.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_RELIABILITY_BOX); - rbox.header.size = htons (sizeof (rbox) + pm->bytes_msg); - rbox.ack_countdown = htonl (0); // FIXME: implement ACK countdown support - rbox.msg_uuid = pm->msg_uuid; + rbox.header.size = htons (sizeof(rbox) + pm->bytes_msg); + rbox.ack_countdown = htonl (0); // FIXME: implement ACK countdown support + + rbox.ack_uuid = pa->ack_uuid; msg = (char *) &bpm[1]; - memcpy (msg, &rbox, sizeof (rbox)); - memcpy (&msg[sizeof (rbox)], &pm[1], pm->bytes_msg); + memcpy (msg, &rbox, sizeof(rbox)); + memcpy (&msg[sizeof(rbox)], &pm[1], pm->bytes_msg); pm->bpm = bpm; return bpm; } @@ -6082,25 +8482,30 @@ static void update_pm_next_attempt (struct PendingMessage *pm, struct GNUNET_TIME_Absolute next_attempt) { - struct Neighbour *neighbour = pm->target; + struct VirtualLink *vl = pm->vl; pm->next_attempt = next_attempt; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Next attempt for message <%llu> set to %s\n", + pm->logging_uuid, + GNUNET_STRINGS_absolute_time_to_string (next_attempt)); + if (NULL == pm->frag_parent) { struct PendingMessage *pos; /* re-insert sort in neighbour list */ - GNUNET_CONTAINER_MDLL_remove (neighbour, - neighbour->pending_msg_head, - neighbour->pending_msg_tail, + GNUNET_CONTAINER_MDLL_remove (vl, + vl->pending_msg_head, + vl->pending_msg_tail, pm); - pos = neighbour->pending_msg_tail; + pos = vl->pending_msg_tail; while ((NULL != pos) && (next_attempt.abs_value_us > pos->next_attempt.abs_value_us)) - pos = pos->prev_neighbour; - GNUNET_CONTAINER_MDLL_insert_after (neighbour, - neighbour->pending_msg_head, - neighbour->pending_msg_tail, + pos = pos->prev_vl; + GNUNET_CONTAINER_MDLL_insert_after (vl, + vl->pending_msg_head, + vl->pending_msg_tail, pos, pm); } @@ -6125,10 +8530,206 @@ update_pm_next_attempt (struct PendingMessage *pm, /** - * We believe we are ready to transmit a message on a queue. Double-checks - * with the queue's "tracker_out" and then gives the message to the - * communicator for transmission (updating the tracker, and re-scheduling - * itself if applicable). + * Context for #select_best_pending_from_link(). + */ +struct PendingMessageScoreContext +{ + /** + * Set to the best message that was found, NULL for none. + */ + struct PendingMessage *best; + + /** + * DVH that @e best should take, or NULL for direct transmission. + */ + struct DistanceVectorHop *dvh; + + /** + * What is the estimated total overhead for this message? + */ + size_t real_overhead; + + /** + * Number of pending messages we seriously considered this time. + */ + unsigned int consideration_counter; + + /** + * Did we have to fragment? + */ + int frag; + + /** + * Did we have to reliability box? + */ + int relb; +}; + + +/** + * Select the best pending message from @a vl for transmission + * via @a queue. + * + * @param sc[in,out] best message so far (NULL for none), plus scoring data + * @param queue the queue that will be used for transmission + * @param vl the virtual link providing the messages + * @param dvh path we are currently considering, or NULL for none + * @param overhead number of bytes of overhead to be expected + * from DV encapsulation (0 for without DV) + */ +static void +select_best_pending_from_link (struct PendingMessageScoreContext *sc, + struct Queue *queue, + struct VirtualLink *vl, + struct DistanceVectorHop *dvh, + size_t overhead) +{ + struct GNUNET_TIME_Absolute now; + + now = GNUNET_TIME_absolute_get (); + for (struct PendingMessage *pos = vl->pending_msg_head; NULL != pos; + pos = pos->next_vl) + { + size_t real_overhead = overhead; + int frag; + int relb; + + if ((NULL != dvh) && (PMT_DV_BOX == pos->pmt)) + continue; /* DV messages must not be DV-routed to next hop! */ + if (pos->next_attempt.abs_value_us > now.abs_value_us) + break; /* too early for all messages, they are sorted by next_attempt */ + if (NULL != pos->qe) + continue; /* not eligible */ + sc->consideration_counter++; + /* determine if we have to fragment, if so add fragmentation + overhead! */ + frag = GNUNET_NO; + if (((0 != queue->mtu) && + (pos->bytes_msg + real_overhead > queue->mtu)) || + (pos->bytes_msg > UINT16_MAX - sizeof(struct + GNUNET_TRANSPORT_SendMessageTo)) + || + (NULL != pos->head_frag /* fragments already exist, should + respect that even if MTU is 0 for + this queue */)) + { + frag = GNUNET_YES; + if (GNUNET_TRANSPORT_CC_RELIABLE == queue->tc->details.communicator.cc) + { + /* FIXME-FRAG-REL-UUID: we could use an optimized, shorter fragmentation + header without the ACK UUID when using a *reliable* channel! */ + } + real_overhead = overhead + sizeof(struct TransportFragmentBoxMessage); + } + /* determine if we have to reliability-box, if so add reliability box + overhead */ + relb = GNUNET_NO; + if ((GNUNET_NO == frag) && + (0 == (pos->prefs & GNUNET_MQ_PREF_UNRELIABLE)) && + (GNUNET_TRANSPORT_CC_RELIABLE != queue->tc->details.communicator.cc)) + { + relb = GNUNET_YES; + real_overhead += sizeof(struct TransportReliabilityBoxMessage); + } + + /* Finally, compare to existing 'best' in sc to see if this 'pos' pending + message would beat it! */ + if (NULL != sc->best) + { + /* CHECK if pos fits queue BETTER (=smaller) than pm, if not: continue; + OPTIMIZE-ME: This is a heuristic, which so far has NOT been + experimentally validated. There may be some huge potential for + improvement here. Also, we right now only compare how well the + given message fits _this_ queue, and do not consider how well other + queues might suit the message. Taking other queues into consideration + may further improve the result, but could also be expensive + in terms of CPU time. */long long sc_score = sc->frag * 40 + sc->relb * 20 + sc->real_overhead; + long long pm_score = frag * 40 + relb * 20 + real_overhead; + long long time_delta = + (sc->best->next_attempt.abs_value_us - pos->next_attempt.abs_value_us) + / 1000LL; + + /* "time_delta" considers which message has been 'ready' for transmission + for longer, if a message has a preference for low latency, increase + the weight of the time_delta by 10x if it is favorable for that message */ + if ((0 != (pos->prefs & GNUNET_MQ_PREF_LOW_LATENCY)) && + (0 != (sc->best->prefs & GNUNET_MQ_PREF_LOW_LATENCY))) + time_delta *= 10; /* increase weight (always, both are low latency) */ + else if ((0 != (pos->prefs & GNUNET_MQ_PREF_LOW_LATENCY)) && + (time_delta > 0)) + time_delta *= + 10; /* increase weight, favors 'pos', which is low latency */ + else if ((0 != (sc->best->prefs & GNUNET_MQ_PREF_LOW_LATENCY)) && + (time_delta < 0)) + time_delta *= + 10; /* increase weight, favors 'sc->best', which is low latency */ + if (0 != queue->mtu) + { + /* Grant bonus if we are bellow MTU, larger bonus the closer we will + be to the MTU */ + if (queue->mtu > sc->real_overhead + sc->best->bytes_msg) + sc_score -= queue->mtu - (sc->real_overhead + sc->best->bytes_msg); + if (queue->mtu > real_overhead + pos->bytes_msg) + pm_score -= queue->mtu - (real_overhead + pos->bytes_msg); + } + if (sc_score + time_delta > pm_score) + continue; /* sc_score larger, keep sc->best */ + } + sc->best = pos; + sc->dvh = dvh; + sc->frag = frag; + sc->relb = relb; + } +} + + +/** + * Function to call to further operate on the now DV encapsulated + * message @a hdr, forwarding it via @a next_hop under respect of + * @a options. + * + * @param cls a `struct PendingMessageScoreContext` + * @param next_hop next hop of the DV path + * @param hdr encapsulated message, technically a `struct TransportDFBoxMessage` + * @param options options of the original message + */ +static void +extract_box_cb (void *cls, + struct Neighbour *next_hop, + const struct GNUNET_MessageHeader *hdr, + enum RouteMessageOptions options) +{ + struct PendingMessageScoreContext *sc = cls; + struct PendingMessage *pm = sc->best; + struct PendingMessage *bpm; + uint16_t bsize = ntohs (hdr->size); + + GNUNET_assert (NULL == pm->bpm); + bpm = GNUNET_malloc (sizeof(struct PendingMessage) + bsize); + bpm->logging_uuid = logging_uuid_gen++; + bpm->pmt = PMT_DV_BOX; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating DV Box %llu for original message %llu (next hop is %s)\n", + bpm->logging_uuid, + pm->logging_uuid, + GNUNET_i2s (&next_hop->pid)); + memcpy (&bpm[1], hdr, bsize); + pm->bpm = bpm; +} + + +/** + * We believe we are ready to transmit a `struct PendingMessage` on a + * queue, the big question is which one! We need to see if there is + * one pending that is allowed by flow control and congestion control + * and (ideally) matches our queue's performance profile. + * + * If such a message is found, we give the message to the communicator + * for transmission (updating the tracker, and re-scheduling ourselves + * if applicable). + * + * If no such message is found, the queue's `idle` field must be set + * to #GNUNET_YES. * * @param cls the `struct Queue` to process transmissions for */ @@ -6137,100 +8738,142 @@ transmit_on_queue (void *cls) { struct Queue *queue = cls; struct Neighbour *n = queue->neighbour; + struct PendingMessageScoreContext sc; struct PendingMessage *pm; - struct PendingMessage *s; - uint32_t overhead; queue->transmit_task = NULL; - if (NULL == (pm = n->pending_msg_head)) + if (NULL == n->vl) { - /* no message pending, nothing to do here! */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Virtual link `%s' is down, cannot have PM for queue `%s'\n", + GNUNET_i2s (&n->pid), + queue->address); + queue->idle = GNUNET_YES; return; } - if (NULL != pm->qe) + memset (&sc, 0, sizeof(sc)); + select_best_pending_from_link (&sc, queue, n->vl, NULL, 0); + if (NULL == sc.best) { - /* message still pending with communciator! - LOGGING-FIXME: Use stats? logging? Should this not be rare? */ - return; - } - schedule_transmit_on_queue (queue, GNUNET_YES); - if (NULL != queue->transmit_task) - return; /* do it later */ - overhead = 0; - if (GNUNET_TRANSPORT_CC_RELIABLE != queue->tc->details.communicator.cc) - overhead += sizeof (struct TransportReliabilityBox); - s = pm; - if ( ( (0 != queue->mtu) && - (pm->bytes_msg + overhead > queue->mtu) ) || - (pm->bytes_msg > UINT16_MAX - sizeof (struct GNUNET_TRANSPORT_SendMessageTo)) || - (NULL != pm->head_frag /* fragments already exist, should - respect that even if MTU is 0 for - this queue */) ) - s = fragment_message (s, - (0 == queue->mtu) - ? UINT16_MAX - - sizeof (struct GNUNET_TRANSPORT_SendMessageTo) - : queue->mtu); - if (NULL == s) - { - /* Fragmentation failed, try next message... */ - schedule_transmit_on_queue (queue, GNUNET_NO); - return; + /* Also look at DVH that have the n as first hop! */ + for (struct DistanceVectorHop *dvh = n->dv_head; NULL != dvh; + dvh = dvh->next_neighbour) + { + select_best_pending_from_link (&sc, + queue, + dvh->dv->vl, + dvh, + sizeof(struct GNUNET_PeerIdentity) + * (1 + dvh->distance) + + sizeof(struct TransportDVBoxMessage) + + sizeof(struct TransportDVBoxPayloadP)); + } } - if (GNUNET_TRANSPORT_CC_RELIABLE != queue->tc->details.communicator.cc) - s = reliability_box_message (s); - if (NULL == s) + if (NULL == sc.best) { - /* Reliability boxing failed, try next message... */ - schedule_transmit_on_queue (queue, GNUNET_NO); + /* no message pending, nothing to do here! */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No pending messages, queue `%s' to %s now idle\n", + queue->address, + GNUNET_i2s (&n->pid)); + queue->idle = GNUNET_YES; return; } - /* Pass 's' for transission to the communicator */ - queue_send_msg (queue, s, &s[1], s->bytes_msg); - // FIXME: do something similar to the logic below - // in defragmentation / reliability ACK handling! - - /* Check if this transmission somehow conclusively finished handing 'pm' - even without any explicit ACKs */ - if ((PMT_CORE == s->pmt) && - (GNUNET_TRANSPORT_CC_RELIABLE == queue->tc->details.communicator.cc)) + /* Given selection in `sc`, do transmission */ + pm = sc.best; + if (NULL != sc.dvh) { - /* Full message sent, and over reliabile channel */ - client_send_response (pm, GNUNET_YES, pm->bytes_msg); + GNUNET_assert (PMT_DV_BOX != pm->pmt); + if (NULL != sc.best->bpm) + { + /* We did this boxing before, but possibly for a different path! + Discard old DV box! OPTIMIZE-ME: we might want to check if + it is the same and then not re-build the message... */ + free_pending_message (sc.best->bpm); + sc.best->bpm = NULL; + } + encapsulate_for_dv (sc.dvh->dv, + 1, + &sc.dvh, + (const struct GNUNET_MessageHeader *) &sc.best[1], + &extract_box_cb, + &sc, + RMO_NONE); + GNUNET_assert (NULL != sc.best->bpm); + pm = sc.best->bpm; + } + if (GNUNET_YES == sc.frag) + { + pm = fragment_message (queue, sc.dvh, pm); + if (NULL == pm) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Fragmentation failed queue %s to %s for <%llu>, trying again\n", + queue->address, + GNUNET_i2s (&n->pid), + sc.best->logging_uuid); + schedule_transmit_on_queue (queue, GNUNET_SCHEDULER_PRIORITY_DEFAULT); + return; + } } - else if ((GNUNET_TRANSPORT_CC_RELIABLE == - queue->tc->details.communicator.cc) && - (PMT_FRAGMENT_BOX == s->pmt)) + else if (GNUNET_YES == sc.relb) { - struct PendingMessage *pos; - - /* Fragment sent over reliabile channel */ - free_fragment_tree (s); - pos = s->frag_parent; - GNUNET_CONTAINER_MDLL_remove (frag, pos->head_frag, pos->tail_frag, s); - GNUNET_free (s); - /* check if subtree is done */ - while ((NULL == pos->head_frag) && (pos->frag_off == pos->bytes_msg) && - (pos != pm)) + pm = reliability_box_message (queue, sc.dvh, pm); + if (NULL == pm) { - s = pos; - pos = s->frag_parent; - GNUNET_CONTAINER_MDLL_remove (frag, pos->head_frag, pos->tail_frag, s); - GNUNET_free (s); + /* Reliability boxing failed, try next message... */ + GNUNET_log ( + GNUNET_ERROR_TYPE_DEBUG, + "Reliability boxing failed queue %s to %s for <%llu>, trying again\n", + queue->address, + GNUNET_i2s (&n->pid), + sc.best->logging_uuid); + schedule_transmit_on_queue (queue, GNUNET_SCHEDULER_PRIORITY_DEFAULT); + return; } + } - /* Was this the last applicable fragmment? */ - if ((NULL == pm->head_frag) && (pm->frag_off == pm->bytes_msg)) - client_send_response ( - pm, - GNUNET_YES, - pm->bytes_msg /* FIXME: calculate and add overheads! */); + /* Pass 'pm' for transission to the communicator */ + GNUNET_log ( + GNUNET_ERROR_TYPE_DEBUG, + "Passing message <%llu> to queue %s for peer %s (considered %u others)\n", + pm->logging_uuid, + queue->address, + GNUNET_i2s (&n->pid), + sc.consideration_counter); + + /* Flow control: increment amount of traffic sent; if we are routing + via DV (and thus the ultimate target of the pending message is for + a different virtual link than the one of the queue), then we need + to use up not only the window of the direct link but also the + flow control window for the DV link! */pm->vl->outbound_fc_window_size_used += pm->bytes_msg; + + if (pm->vl != queue->neighbour->vl) + { + /* If the virtual link of the queue differs, this better be distance + vector routing! */ + GNUNET_assert (NULL != sc.dvh); + /* If we do distance vector routing, we better not do this for a + message that was itself DV-routed */ + GNUNET_assert (PMT_DV_BOX != sc.best->pmt); + /* We use the size of the unboxed message here, to avoid counting + the DV-Box header which is eaten up on the way by intermediaries */ + queue->neighbour->vl->outbound_fc_window_size_used += sc.best->bytes_msg; } - else if (PMT_CORE != pm->pmt) + else { - /* This was an acknowledgement of some type, always free */ - free_pending_message (pm); + GNUNET_assert (NULL == sc.dvh); + } + + queue_send_msg (queue, pm, &pm[1], pm->bytes_msg); + + /* Check if this transmission somehow conclusively finished handing 'pm' + even without any explicit ACKs */ + if ((PMT_CORE == pm->pmt) || + (GNUNET_TRANSPORT_CC_RELIABLE == queue->tc->details.communicator.cc)) + { + completed_pending_message (pm); } else { @@ -6239,83 +8882,17 @@ transmit_on_queue (void *cls) characteristics (i.e. RTT); it takes one RTT for the message to arrive and the ACK to come back in the best case; but the other side is allowed to delay ACKs by 2 RTTs, so we use 4 RTT before - retransmitting. Note that in the future this heuristic should - likely be improved further (measure RTT stability, consider - message urgency and size when delaying ACKs, etc.) */ - update_pm_next_attempt (s, + retransmitting. + + OPTIMIZE: Note that in the future this heuristic should likely + be improved further (measure RTT stability, consider message + urgency and size when delaying ACKs, etc.) */update_pm_next_attempt (pm, GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply (queue->rtt, 4))); + GNUNET_TIME_relative_multiply (queue->pd.aged_rtt, + 4))); } - /* finally, re-schedule queue transmission task itself */ - schedule_transmit_on_queue (queue, GNUNET_NO); -} - - -/** - * Bandwidth tracker informs us that the delay until we - * can transmit again changed. - * - * @param cls a `struct Queue` for which the delay changed - */ -static void -tracker_update_out_cb (void *cls) -{ - struct Queue *queue = cls; - struct Neighbour *n = queue->neighbour; - - if (NULL == n->pending_msg_head) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Bandwidth allocation updated for empty transmission queue `%s'\n", - queue->address); - return; /* no message pending, nothing to do here! */ - } - GNUNET_SCHEDULER_cancel (queue->transmit_task); - queue->transmit_task = NULL; - schedule_transmit_on_queue (queue, GNUNET_NO); -} - - -/** - * Bandwidth tracker informs us that excessive outbound bandwidth was - * allocated which is not being used. - * - * @param cls a `struct Queue` for which the excess was noted - */ -static void -tracker_excess_out_cb (void *cls) -{ - (void) cls; - - /* FIXME: trigger excess bandwidth report to core? Right now, - this is done internally within transport_api2_core already, - but we probably want to change the logic and trigger it - from here via a message instead! */ - /* TODO: maybe inform someone at this point? */ - GNUNET_STATISTICS_update (GST_stats, - "# Excess outbound bandwidth reported", - 1, - GNUNET_NO); -} - - -/** - * Bandwidth tracker informs us that excessive inbound bandwidth was allocated - * which is not being used. - * - * @param cls a `struct Queue` for which the excess was noted - */ -static void -tracker_excess_in_cb (void *cls) -{ - (void) cls; - - /* TODO: maybe inform somone at this point? */ - GNUNET_STATISTICS_update (GST_stats, - "# Excess inbound bandwidth reported", - 1, - GNUNET_NO); + schedule_transmit_on_queue (queue, GNUNET_SCHEDULER_PRIORITY_DEFAULT); } @@ -6345,6 +8922,10 @@ handle_del_queue_message (void *cls, if ((dqm->qid != queue->qid) || (0 != GNUNET_memcmp (&dqm->receiver, &neighbour->pid))) continue; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Dropped queue %s to peer %s\n", + queue->address, + GNUNET_i2s (&neighbour->pid)); free_queue (queue); GNUNET_SERVICE_client_continue (tc->client); return; @@ -6404,6 +8985,12 @@ handle_send_message_ack (void *cls, qe); qe->queue->queue_length--; tc->details.communicator.total_queue_length--; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received ACK on queue %s to peer %s (new length: %u/%u)\n", + qe->queue->address, + GNUNET_i2s (&qe->queue->neighbour->pid), + qe->queue->queue_length, + tc->details.communicator.total_queue_length); GNUNET_SERVICE_client_continue (tc->client); /* if applicable, resume transmissions that waited on ACK */ @@ -6420,7 +9007,7 @@ handle_send_message_ack (void *cls, for (struct Queue *queue = tc->details.communicator.queue_head; NULL != queue; queue = queue->next_client) - schedule_transmit_on_queue (queue, GNUNET_NO); + schedule_transmit_on_queue (queue, GNUNET_SCHEDULER_PRIORITY_DEFAULT); } else if (QUEUE_LENGTH_LIMIT - 1 == qe->queue->queue_length) { @@ -6429,32 +9016,21 @@ handle_send_message_ack (void *cls, "# Transmission throttled due to queue queue limit", -1, GNUNET_NO); - schedule_transmit_on_queue (qe->queue, GNUNET_NO); + schedule_transmit_on_queue (qe->queue, GNUNET_SCHEDULER_PRIORITY_DEFAULT); } if (NULL != (pm = qe->pm)) { - struct Neighbour *n; + struct VirtualLink *vl; GNUNET_assert (qe == pm->qe); pm->qe = NULL; /* If waiting for this communicator may have blocked transmission of pm on other queues for this neighbour, force schedule transmit on queue for queues of the neighbour */ - n = pm->target; - if (n->pending_msg_head == pm) - { - for (struct Queue *queue = n->queue_head; NULL != queue; - queue = queue->next_neighbour) - schedule_transmit_on_queue (queue, GNUNET_NO); - } - if (GNUNET_OK != ntohl (sma->status)) - { - GNUNET_log ( - GNUNET_ERROR_TYPE_INFO, - "Queue failed in transmission, will try retransmission immediately\n"); - update_pm_next_attempt (pm, GNUNET_TIME_UNIT_ZERO_ABS); - } + vl = pm->vl; + if (vl->pending_msg_head == pm) + check_vl_transmission (vl); } GNUNET_free (qe); } @@ -6481,10 +9057,10 @@ notify_client_queues (void *cls, for (struct Queue *q = neighbour->queue_head; NULL != q; q = q->next_neighbour) { - struct MonitorEvent me = {.rtt = q->rtt, - .cs = q->cs, - .num_msg_pending = q->num_msg_pending, - .num_bytes_pending = q->num_bytes_pending}; + struct MonitorEvent me = { .rtt = q->pd.aged_rtt, + .cs = q->cs, + .num_msg_pending = q->num_msg_pending, + .num_bytes_pending = q->num_bytes_pending }; notify_monitor (tc, pid, q->address, q->nt, &me); } @@ -6564,7 +9140,7 @@ suggest_to_connect (const struct GNUNET_PeerIdentity *pid, const char *address) prefix = GNUNET_HELLO_address_to_prefix (address); if (NULL == prefix) { - GNUNET_break (0); /* We got an invalid address!? */ + GNUNET_break (0); /* We got an invalid address!? */ return; } tc = lookup_communicator (prefix); @@ -6574,6 +9150,11 @@ suggest_to_connect (const struct GNUNET_PeerIdentity *pid, const char *address) "# Suggestions ignored due to missing communicator", 1, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Cannot connect to %s at `%s', no matching communicator present\n", + GNUNET_i2s (pid), + address); + GNUNET_free (prefix); return; } /* forward suggestion for queue creation to communicator */ @@ -6582,6 +9163,7 @@ suggest_to_connect (const struct GNUNET_PeerIdentity *pid, const char *address) (unsigned int) idgen, prefix, address); + GNUNET_free (prefix); alen = strlen (address) + 1; env = GNUNET_MQ_msg_extra (cqm, alen, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE); @@ -6602,16 +9184,20 @@ suggest_to_connect (const struct GNUNET_PeerIdentity *pid, const char *address) static void validation_transmit_on_queue (struct Queue *q, struct ValidationState *vs) { - struct TransportValidationChallenge tvc; + struct TransportValidationChallengeMessage tvc; - vs->last_challenge_use = GNUNET_TIME_absolute_get (); + vs->last_challenge_use = GNUNET_TIME_absolute_get_monotonic (GST_cfg); tvc.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_VALIDATION_CHALLENGE); - tvc.header.size = htons (sizeof (tvc)); + tvc.header.size = htons (sizeof(tvc)); tvc.reserved = htonl (0); tvc.challenge = vs->challenge; tvc.sender_time = GNUNET_TIME_absolute_hton (vs->last_challenge_use); - queue_send_msg (q, NULL, &tvc, sizeof (tvc)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Sending address validation challenge %s to %s\n", + GNUNET_sh2s (&tvc.challenge.value), + GNUNET_i2s (&q->neighbour->pid)); + queue_send_msg (q, NULL, &tvc, sizeof(tvc)); } @@ -6638,8 +9224,12 @@ validation_start_cb (void *cls) vs = GNUNET_CONTAINER_heap_peek (validation_heap); } if (NULL == vs) - return; /* woopsie, no more addresses known, should only - happen if we're really a lonely peer */ + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Address validation task not scheduled anymore, nothing to do\n"); + return; /* woopsie, no more addresses known, should only + happen if we're really a lonely peer */ + } q = find_queue (&vs->pid, vs->address); if (NULL == q) { @@ -6652,6 +9242,10 @@ validation_start_cb (void *cls) vs->challenge_backoff = GNUNET_TIME_randomized_backoff (vs->challenge_backoff, MAX_VALIDATION_CHALLENGE_FREQ); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Address validation task will run again in %s\n", + GNUNET_STRINGS_relative_time_to_string (vs->challenge_backoff, + GNUNET_YES)); update_next_challenge_time (vs, GNUNET_TIME_relative_to_absolute ( vs->challenge_backoff)); @@ -6713,9 +9307,9 @@ check_connection_quality (void *cls, ctx->num_queues++; if (0 == ctx->k--) ctx->q = q; - /* OPTIMIZE-FIXME: in the future, add reliability / goodput + /* FIXME-CONQ-STATISTICS: in the future, add reliability / goodput statistics and consider those as well here? */ - if (q->rtt.rel_value_us < DV_QUALITY_RTT_THRESHOLD.rel_value_us) + if (q->pd.aged_rtt.rel_value_us < DV_QUALITY_RTT_THRESHOLD.rel_value_us) do_inc = GNUNET_YES; } if (GNUNET_YES == do_inc) @@ -6740,7 +9334,7 @@ start_dv_learn (void *cls) { struct LearnLaunchEntry *lle; struct QueueQualityContext qqc; - struct TransportDVLearn dvl; + struct TransportDVLearnMessage dvl; (void) cls; dvlearn_task = NULL; @@ -6759,6 +9353,10 @@ start_dv_learn (void *cls) /* scale our retries by how far we are above the threshold */ factor = qqc.quality_count / DV_LEARN_QUALITY_THRESHOLD; delay = GNUNET_TIME_relative_multiply (DV_LEARN_BASE_FREQUENCY, factor); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "At connection quality %u, will launch DV learn in %s\n", + qqc.quality_count, + GNUNET_STRINGS_relative_time_to_string (delay, GNUNET_YES)); dvlearn_task = GNUNET_SCHEDULER_add_delayed (delay, &start_dv_learn, NULL); return; } @@ -6769,7 +9367,7 @@ start_dv_learn (void *cls) lle = lle_tail; GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multishortmap_remove (dvlearn_map, - &lle->challenge, + &lle->challenge.value, lle)); GNUNET_CONTAINER_DLL_remove (lle_head, lle_tail, lle); GNUNET_free (lle); @@ -6778,28 +9376,36 @@ start_dv_learn (void *cls) lle = GNUNET_new (struct LearnLaunchEntry); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &lle->challenge, - sizeof (lle->challenge)); + sizeof(lle->challenge)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting launch DV learn with challenge %s\n", + GNUNET_sh2s (&lle->challenge.value)); GNUNET_CONTAINER_DLL_insert (lle_head, lle_tail, lle); GNUNET_break (GNUNET_YES == GNUNET_CONTAINER_multishortmap_put ( dvlearn_map, - &lle->challenge, + &lle->challenge.value, lle, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); dvl.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_DV_LEARN); - dvl.header.size = htons (sizeof (dvl)); + dvl.header.size = htons (sizeof(dvl)); dvl.num_hops = htons (0); dvl.bidirectional = htons (0); dvl.non_network_delay = GNUNET_TIME_relative_hton (GNUNET_TIME_UNIT_ZERO); + dvl.monotonic_time = + GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_monotonic (GST_cfg)); { - struct DvInitPS dvip = {.purpose.purpose = htonl ( - GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR), - .purpose.size = htonl (sizeof (dvip)), - .challenge = lle->challenge}; + struct DvInitPS dvip = { + .purpose.purpose = htonl ( + GNUNET_SIGNATURE_PURPOSE_TRANSPORT_DV_INITIATOR), + .purpose.size = htonl (sizeof(dvip)), + .monotonic_time = dvl.monotonic_time, + .challenge = lle->challenge + }; - GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, - &dvip.purpose, - &dvl.init_sig)); + GNUNET_CRYPTO_eddsa_sign (GST_my_private_key, + &dvip, + &dvl.init_sig); } dvl.initiator = GST_my_identity; dvl.challenge = lle->challenge; @@ -6816,7 +9422,7 @@ start_dv_learn (void *cls) /* Do this as close to transmission time as possible! */ lle->launch_time = GNUNET_TIME_absolute_get (); - queue_send_msg (qqc.q, NULL, &dvl, sizeof (dvl)); + queue_send_msg (qqc.q, NULL, &dvl, sizeof(dvl)); /* reschedule this job, randomizing the time it runs (but no actual backoff!) */ dvlearn_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_randomize ( @@ -6855,6 +9461,43 @@ check_validation_request_pending (void *cls, } +/** + * Function called with the monotonic time of a DV initiator + * by PEERSTORE. Updates the time. + * + * @param cls a `struct Neighbour` + * @param record the information found, NULL for the last call + * @param emsg error message + */ +static void +neighbour_dv_monotime_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct Neighbour *n = cls; + struct GNUNET_TIME_AbsoluteNBO *mtbe; + + (void) emsg; + if (NULL == record) + { + /* we're done with #neighbour_dv_monotime_cb() invocations, + continue normal processing */ + n->get = NULL; + n->dv_monotime_available = GNUNET_YES; + return; + } + if (sizeof(*mtbe) != record->value_size) + { + GNUNET_break (0); + return; + } + mtbe = record->value; + n->last_dv_learn_monotime = + GNUNET_TIME_absolute_max (n->last_dv_learn_monotime, + GNUNET_TIME_absolute_ntoh (*mtbe)); +} + + /** * New queue became available. Process the request. * @@ -6871,7 +9514,7 @@ handle_add_queue_message (void *cls, const char *addr; uint16_t addr_len; - if (ntohl (aqm->mtu) <= sizeof (struct TransportFragmentBox)) + if (ntohl (aqm->mtu) <= sizeof(struct TransportFragmentBoxMessage)) { /* MTU so small as to be useless for transmissions, required for #fragment_message()! */ @@ -6883,7 +9526,6 @@ handle_add_queue_message (void *cls, if (NULL == neighbour) { neighbour = GNUNET_new (struct Neighbour); - neighbour->earliest_timeout = GNUNET_TIME_UNIT_FOREVER_ABS; neighbour->pid = aqm->receiver; GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multipeermap_put ( @@ -6891,37 +9533,35 @@ handle_add_queue_message (void *cls, &neighbour->pid, neighbour, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - } - addr_len = ntohs (aqm->header.size) - sizeof (*aqm); + neighbour->get = + GNUNET_PEERSTORE_iterate (peerstore, + "transport", + &neighbour->pid, + GNUNET_PEERSTORE_TRANSPORT_DVLEARN_MONOTIME, + &neighbour_dv_monotime_cb, + neighbour); + } + addr_len = ntohs (aqm->header.size) - sizeof(*aqm); addr = (const char *) &aqm[1]; - - queue = GNUNET_malloc (sizeof (struct Queue) + addr_len); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New queue %s to %s available with QID %llu\n", + addr, + GNUNET_i2s (&aqm->receiver), + (unsigned long long) aqm->qid); + queue = GNUNET_malloc (sizeof(struct Queue) + addr_len); queue->tc = tc; queue->address = (const char *) &queue[1]; - queue->rtt = GNUNET_TIME_UNIT_FOREVER_REL; + queue->pd.aged_rtt = GNUNET_TIME_UNIT_FOREVER_REL; queue->qid = aqm->qid; queue->mtu = ntohl (aqm->mtu); queue->nt = (enum GNUNET_NetworkType) ntohl (aqm->nt); queue->cs = (enum GNUNET_TRANSPORT_ConnectionStatus) ntohl (aqm->cs); queue->neighbour = neighbour; - GNUNET_BANDWIDTH_tracker_init2 (&queue->tracker_in, - &tracker_update_in_cb, - queue, - GNUNET_BANDWIDTH_ZERO, - GNUNET_CONSTANTS_MAX_BANDWIDTH_CARRY_S, - &tracker_excess_in_cb, - queue); - GNUNET_BANDWIDTH_tracker_init2 (&queue->tracker_out, - &tracker_update_out_cb, - queue, - GNUNET_BANDWIDTH_ZERO, - GNUNET_CONSTANTS_MAX_BANDWIDTH_CARRY_S, - &tracker_excess_out_cb, - queue); + queue->idle = GNUNET_YES; memcpy (&queue[1], addr, addr_len); /* notify monitors about new queue */ { - struct MonitorEvent me = {.rtt = queue->rtt, .cs = queue->cs}; + struct MonitorEvent me = { .rtt = queue->pd.aged_rtt, .cs = queue->cs }; notify_monitors (&neighbour->pid, queue->address, queue->nt, &me); } @@ -6935,10 +9575,12 @@ handle_add_queue_message (void *cls, queue); /* check if valdiations are waiting for the queue */ (void) - GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, - &aqm->receiver, - &check_validation_request_pending, - queue); + GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, + &aqm->receiver, + &check_validation_request_pending, + queue); + /* look for traffic for this queue */ + schedule_transmit_on_queue (queue, GNUNET_SCHEDULER_PRIORITY_DEFAULT); /* might be our first queue, try launching DV learning */ if (NULL == dvlearn_task) dvlearn_task = GNUNET_SCHEDULER_add_now (&start_dv_learn, NULL); @@ -6977,8 +9619,8 @@ handle_queue_create_ok (void *cls, /** - * Communicator tells us that our request to create a queue failed. This usually - * indicates that the provided address is simply invalid or that the + * Communicator tells us that our request to create a queue failed. This + * usually indicates that the provided address is simply invalid or that the * communicator's resources are exhausted. * * @param cls the `struct TransportClient` @@ -7040,137 +9682,17 @@ handle_suggest_cancel (void *cls, const struct ExpressPreferenceMessage *msg) } -/** - * Check #GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_CONSIDER_VERIFY - * messages. We do nothing here, real verification is done later. - * - * @param cls a `struct TransportClient *` - * @param msg message to verify - * @return #GNUNET_OK - */ -static int -check_address_consider_verify ( - void *cls, - const struct GNUNET_TRANSPORT_AddressToVerify *hdr) -{ - (void) cls; - (void) hdr; - return GNUNET_OK; -} - - -/** - * Closure for #check_known_address. - */ -struct CheckKnownAddressContext -{ - /** - * Set to the address we are looking for. - */ - const char *address; - - /** - * Set to a matching validation state, if one was found. - */ - struct ValidationState *vs; -}; - - -/** - * Test if the validation state in @a value matches the - * address from @a cls. - * - * @param cls a `struct CheckKnownAddressContext` - * @param pid unused (must match though) - * @param value a `struct ValidationState` - * @return #GNUNET_OK if not matching, #GNUNET_NO if match found - */ -static int -check_known_address (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) -{ - struct CheckKnownAddressContext *ckac = cls; - struct ValidationState *vs = value; - - (void) pid; - if (0 != strcmp (vs->address, ckac->address)) - return GNUNET_OK; - ckac->vs = vs; - return GNUNET_NO; -} - - -/** - * Start address validation. - * - * @param pid peer the @a address is for - * @param address an address to reach @a pid (presumably) - * @param expiration when did @a pid claim @a address will become invalid - */ -static void -start_address_validation (const struct GNUNET_PeerIdentity *pid, - const char *address, - struct GNUNET_TIME_Absolute expiration) -{ - struct GNUNET_TIME_Absolute now; - struct ValidationState *vs; - struct CheckKnownAddressContext ckac = {.address = address, .vs = NULL}; - - if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us) - return; /* expired */ - (void) GNUNET_CONTAINER_multipeermap_get_multiple (validation_map, - pid, - &check_known_address, - &ckac); - if (NULL != (vs = ckac.vs)) - { - /* if 'vs' is not currently valid, we need to speed up retrying the - * validation */ - if (vs->validated_until.abs_value_us < vs->next_challenge.abs_value_us) - { - /* reduce backoff as we got a fresh advertisement */ - vs->challenge_backoff = - GNUNET_TIME_relative_min (FAST_VALIDATION_CHALLENGE_FREQ, - GNUNET_TIME_relative_divide (vs->challenge_backoff, - 2)); - update_next_challenge_time (vs, - GNUNET_TIME_relative_to_absolute ( - vs->challenge_backoff)); - } - return; - } - now = GNUNET_TIME_absolute_get (); - vs = GNUNET_new (struct ValidationState); - vs->pid = *pid; - vs->valid_until = expiration; - vs->first_challenge_use = now; - vs->validation_rtt = GNUNET_TIME_UNIT_FOREVER_REL; - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &vs->challenge, - sizeof (vs->challenge)); - vs->address = GNUNET_strdup (address); - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multipeermap_put ( - validation_map, - &vs->pid, - vs, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); - update_next_challenge_time (vs, now); -} - - /** * Function called by PEERSTORE for each matching record. * - * @param cls closure + * @param cls closure, a `struct PeerRequest` * @param record peerstore record information * @param emsg error message, or NULL if no errors */ static void -handle_hello (void *cls, - const struct GNUNET_PEERSTORE_Record *record, - const char *emsg) +handle_hello_for_client (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) { struct PeerRequest *pr = cls; const char *val; @@ -7188,9 +9710,7 @@ handle_hello (void *cls, GNUNET_break (0); return; } - start_address_validation (&pr->pid, - (const char *) record->value, - record->expiry); + start_address_validation (&pr->pid, (const char *) record->value); } @@ -7228,12 +9748,12 @@ handle_suggest (void *cls, const struct ExpressPreferenceMessage *msg) pr->tc = tc; pr->pid = msg->peer; pr->bw = msg->bw; - pr->pk = (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk); + pr->pk = (enum GNUNET_MQ_PriorityPreferences) ntohl (msg->pk); if (GNUNET_YES != GNUNET_CONTAINER_multipeermap_put ( - tc->details.application.requests, - &pr->pid, - pr, - GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) + tc->details.application.requests, + &pr->pid, + pr, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) { GNUNET_break (0); GNUNET_free (pr); @@ -7244,51 +9764,12 @@ handle_suggest (void *cls, const struct ExpressPreferenceMessage *msg) "transport", &pr->pid, GNUNET_PEERSTORE_TRANSPORT_URLADDRESS_KEY, - &handle_hello, + &handle_hello_for_client, pr); GNUNET_SERVICE_client_continue (tc->client); } -/** - * Given another peers address, consider checking it for validity - * and then adding it to the Peerstore. - * - * @param cls a `struct TransportClient` - * @param hdr message containing the raw address data and - * signature in the body, see #GNUNET_HELLO_extract_address() - */ -static void -handle_address_consider_verify ( - void *cls, - const struct GNUNET_TRANSPORT_AddressToVerify *hdr) -{ - struct TransportClient *tc = cls; - char *address; - enum GNUNET_NetworkType nt; - struct GNUNET_TIME_Absolute expiration; - - (void) cls; - // OPTIMIZE-FIXME: checking that we know this address already should - // be done BEFORE checking the signature => HELLO API change! - // OPTIMIZE-FIXME: pre-check: rate-limit signature verification / validation?! - address = - GNUNET_HELLO_extract_address (&hdr[1], - ntohs (hdr->header.size) - sizeof (*hdr), - &hdr->peer, - &nt, - &expiration); - if (NULL == address) - { - GNUNET_break_op (0); - return; - } - start_address_validation (&hdr->peer, address, expiration); - GNUNET_free (address); - GNUNET_SERVICE_client_continue (tc->client); -} - - /** * Check #GNUNET_MESSAGE_TYPE_TRANSPORT_REQUEST_HELLO_VALIDATION * messages. @@ -7320,9 +9801,7 @@ handle_request_hello_validation (void *cls, { struct TransportClient *tc = cls; - start_address_validation (&m->peer, - (const char *) &m[1], - GNUNET_TIME_absolute_ntoh (m->expiration)); + start_address_validation (&m->peer, (const char *) &m[1]); GNUNET_SERVICE_client_continue (tc->client); } @@ -7344,7 +9823,7 @@ free_neighbour_cb (void *cls, (void) cls; (void) pid; - GNUNET_break (0); // should this ever happen? + GNUNET_break (0); // should this ever happen? free_neighbour (neighbour); return GNUNET_OK; @@ -7375,45 +9854,65 @@ free_dv_routes_cb (void *cls, /** - * Free ephemeral entry. + * Free validation state. * * @param cls NULL * @param pid unused - * @param value a `struct EphemeralCacheEntry` + * @param value a `struct ValidationState` * @return #GNUNET_OK (always) */ static int -free_ephemeral_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) +free_validation_state_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) { - struct EphemeralCacheEntry *ece = value; + struct ValidationState *vs = value; (void) cls; (void) pid; - free_ephemeral (ece); + free_validation_state (vs); return GNUNET_OK; } /** - * Free validation state. + * Free pending acknowledgement. + * + * @param cls NULL + * @param key unused + * @param value a `struct PendingAcknowledgement` + * @return #GNUNET_OK (always) + */ +static int +free_pending_ack_cb (void *cls, const struct GNUNET_Uuid *key, void *value) +{ + struct PendingAcknowledgement *pa = value; + + (void) cls; + (void) key; + free_pending_acknowledgement (pa); + return GNUNET_OK; +} + + +/** + * Free acknowledgement cummulator. * * @param cls NULL * @param pid unused - * @param value a `struct ValidationState` + * @param value a `struct AcknowledgementCummulator` * @return #GNUNET_OK (always) */ static int -free_validation_state_cb (void *cls, - const struct GNUNET_PeerIdentity *pid, - void *value) +free_ack_cummulator_cb (void *cls, + const struct GNUNET_PeerIdentity *pid, + void *value) { - struct ValidationState *vs = value; + struct AcknowledgementCummulator *ac = value; (void) cls; (void) pid; - free_validation_state (vs); + GNUNET_free (ac); return GNUNET_OK; } @@ -7428,13 +9927,9 @@ static void do_shutdown (void *cls) { struct LearnLaunchEntry *lle; + (void) cls; - if (NULL != ephemeral_task) - { - GNUNET_SCHEDULER_cancel (ephemeral_task); - ephemeral_task = NULL; - } GNUNET_CONTAINER_multipeermap_iterate (neighbours, &free_neighbour_cb, NULL); if (NULL != peerstore) { @@ -7451,13 +9946,35 @@ do_shutdown (void *cls) GNUNET_free (GST_my_private_key); GST_my_private_key = NULL; } + GNUNET_CONTAINER_multipeermap_iterate (ack_cummulators, + &free_ack_cummulator_cb, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (ack_cummulators); + ack_cummulators = NULL; + GNUNET_CONTAINER_multiuuidmap_iterate (pending_acks, + &free_pending_ack_cb, + NULL); + GNUNET_CONTAINER_multiuuidmap_destroy (pending_acks); + pending_acks = NULL; + GNUNET_break (0 == GNUNET_CONTAINER_multipeermap_size (neighbours)); GNUNET_CONTAINER_multipeermap_destroy (neighbours); neighbours = NULL; + GNUNET_break (0 == GNUNET_CONTAINER_multipeermap_size (links)); + GNUNET_CONTAINER_multipeermap_destroy (links); + links = NULL; + GNUNET_CONTAINER_multipeermap_iterate (backtalkers, + &free_backtalker_cb, + NULL); + GNUNET_CONTAINER_multipeermap_destroy (backtalkers); + backtalkers = NULL; GNUNET_CONTAINER_multipeermap_iterate (validation_map, &free_validation_state_cb, NULL); GNUNET_CONTAINER_multipeermap_destroy (validation_map); validation_map = NULL; + while (NULL != ir_head) + free_incoming_request (ir_head); + GNUNET_assert (0 == ir_total); while (NULL != (lle = lle_head)) { GNUNET_CONTAINER_DLL_remove (lle_head, lle_tail, lle); @@ -7470,13 +9987,6 @@ do_shutdown (void *cls) GNUNET_CONTAINER_multipeermap_iterate (dv_routes, &free_dv_routes_cb, NULL); GNUNET_CONTAINER_multipeermap_destroy (dv_routes); dv_routes = NULL; - GNUNET_CONTAINER_multipeermap_iterate (ephemeral_map, - &free_ephemeral_cb, - NULL); - GNUNET_CONTAINER_multipeermap_destroy (ephemeral_map); - ephemeral_map = NULL; - GNUNET_CONTAINER_heap_destroy (ephemeral_heap); - ephemeral_heap = NULL; } @@ -7495,12 +10005,14 @@ run (void *cls, (void) cls; (void) service; /* setup globals */ + hello_mono_time = GNUNET_TIME_absolute_get_monotonic (c); GST_cfg = c; + backtalkers = GNUNET_CONTAINER_multipeermap_create (16, GNUNET_YES); + pending_acks = GNUNET_CONTAINER_multiuuidmap_create (32768, GNUNET_YES); + ack_cummulators = GNUNET_CONTAINER_multipeermap_create (256, GNUNET_YES); neighbours = GNUNET_CONTAINER_multipeermap_create (1024, GNUNET_YES); + links = GNUNET_CONTAINER_multipeermap_create (512, GNUNET_YES); dv_routes = GNUNET_CONTAINER_multipeermap_create (1024, GNUNET_YES); - ephemeral_map = GNUNET_CONTAINER_multipeermap_create (32, GNUNET_YES); - ephemeral_heap = - GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN); dvlearn_map = GNUNET_CONTAINER_multishortmap_create (2 * MAX_DV_LEARN_PENDING, GNUNET_YES); validation_map = GNUNET_CONTAINER_multipeermap_create (1024, GNUNET_YES); @@ -7566,6 +10078,10 @@ GNUNET_SERVICE_MAIN ( GNUNET_MESSAGE_TYPE_TRANSPORT_SEND, struct OutboundMessage, NULL), + GNUNET_MQ_hd_fixed_size (client_recv_ok, + GNUNET_MESSAGE_TYPE_TRANSPORT_RECV_OK, + struct RecvOkMessage, + NULL), /* communication with communicators */ GNUNET_MQ_hd_var_size (communicator_available, GNUNET_MESSAGE_TYPE_TRANSPORT_NEW_COMMUNICATOR, @@ -7599,10 +10115,6 @@ GNUNET_SERVICE_MAIN ( GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP, struct GNUNET_TRANSPORT_AddQueueMessage, NULL), - GNUNET_MQ_hd_var_size (address_consider_verify, - GNUNET_MESSAGE_TYPE_TRANSPORT_ADDRESS_CONSIDER_VERIFY, - struct GNUNET_TRANSPORT_AddressToVerify, - NULL), GNUNET_MQ_hd_fixed_size (del_queue_message, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_TEARDOWN, struct GNUNET_TRANSPORT_DelQueueMessage,