From 97782d1a85e43d55626b150c06b6479afd8d20d6 Mon Sep 17 00:00:00 2001 From: t3sserakt Date: Tue, 24 Nov 2020 12:36:05 +0100 Subject: - added inband udp communicator rekey logic --- src/transport/gnunet-communicator-udp.c | 810 +++++++++++++++++---- src/transport/test_communicator_basic.c | 31 +- .../test_communicator_udp_rekey_peer1.conf | 8 + .../test_communicator_udp_rekey_peer2.conf | 8 + 4 files changed, 706 insertions(+), 151 deletions(-) diff --git a/src/transport/gnunet-communicator-udp.c b/src/transport/gnunet-communicator-udp.c index e967e8e9a..849adf4ab 100644 --- a/src/transport/gnunet-communicator-udp.c +++ b/src/transport/gnunet-communicator-udp.c @@ -51,7 +51,7 @@ /** * How often do we rekey based on time (at least) */ -#define REKEY_TIME_INTERVAL GNUNET_TIME_UNIT_DAYS +#define DEFAULT_REKEY_TIME_INTERVAL GNUNET_TIME_UNIT_DAYS /** * How long do we wait until we must have received the initial KX? @@ -207,6 +207,11 @@ struct InitialKX * key derived from the handshake with sequence number zero. */ char gcm_tag[GCM_TAG_SIZE]; + + /** + * A flag indicating, if the sender is doing rekeying. + */ + int rekeying; }; @@ -339,8 +344,42 @@ struct UDPBox * `struct UdpHandshakeSignature`. */ char gcm_tag[GCM_TAG_SIZE]; + + /** + * A flag indicating, if the sender is doing rekeying. + */ + int rekeying; }; +/** + * UDP message box. Always sent encrypted, only allowed after + * the receiver sent a `struct UDPAck` for the base key! + */ +struct UDPRekey +{ + /** + * Key and IV identification code. KDF applied to an acknowledged + * base key and a sequence number. Sequence numbers must be used + * monotonically increasing up to the maximum specified in + * `struct UDPAck`. Without further `struct UDPAck`s, the sender + * must fall back to sending handshakes! + */ + struct GNUNET_ShortHashCode kid; + + /** + * 128-bit authentication tag for the following encrypted message, + * from GCM. MAC starts at the @e body_start that follows and + * extends until the end of the UDP payload. If the @e hmac is + * wrong, the receiver should check if the message might be a + * `struct UdpHandshakeSignature`. + */ + char gcm_tag[GCM_TAG_SIZE]; + + /** + * Sender's identity + */ + struct GNUNET_PeerIdentity sender; +}; GNUNET_NETWORK_STRUCT_END @@ -470,6 +509,11 @@ struct SharedSecret */ struct SenderAddress { + /** + * Flag indicating sender is initiated rekeying for this receiver. + */ + int rekeying; + /** * To whom are we talking to. */ @@ -520,6 +564,11 @@ struct SenderAddress * Which network type does this queue use? */ enum GNUNET_NetworkType nt; + + /** + * sender_destroy already called on sender. + */ + int sender_destroy_called; }; @@ -529,6 +578,37 @@ struct SenderAddress */ struct ReceiverAddress { + + /** + * Shared secret we use with @e target for rekeying. + */ + struct SharedSecret *ss_rekey; + + /** + * Acks available when we started rekeying. + */ + unsigned int rekey_acks_available; + + /** + * Send bytes for this receiver address. + */ + uint64_t rekey_send_bytes; + + /** + * Timeout for this receiver address. + */ + struct GNUNET_TIME_Absolute rekey_timeout; + + /** + * Flag indicating sender is initiated rekeying for this receiver. + */ + int rekeying; + + /** + * Numer of kce we retain for sending the rekeying shared secret. + */ + int number_rekeying_kce; + /** * To whom are we talking to. */ @@ -615,8 +695,12 @@ struct ReceiverAddress * Which network type does this queue use? */ enum GNUNET_NetworkType nt; -}; + /** + * receiver_destroy already called on receiver. + */ + int receiver_destroy_called; +}; /** * Interface we broadcast our presence on. @@ -671,9 +755,9 @@ struct BroadcastInterface }; /** - * Timeout for this receiver address. - */ -struct GNUNET_TIME_Absolute *rekey_timeout; + * The rekey interval + */ +static struct GNUNET_TIME_Relative rekey_interval; /** * Shared secret we finished the last kce working queue for. @@ -700,6 +784,11 @@ static struct GNUNET_SCHEDULER_Task *timeout_task; */ static struct GNUNET_SCHEDULER_Task *kce_task; +/** + * ID of kce rekey working queue task + */ +static struct GNUNET_SCHEDULER_Task *kce_task_rekey; + /** * Is the kce_task finished? */ @@ -832,26 +921,28 @@ bi_destroy (struct BroadcastInterface *bi) static void receiver_destroy (struct ReceiverAddress *receiver) { - struct GNUNET_MQ_Handle *mq; + + receiver->receiver_destroy_called = GNUNET_YES; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting receiver for peer `%s'\n", GNUNET_i2s (&receiver->target)); - if (NULL != (mq = receiver->kx_mq)) + /*if (NULL != (mq = receiver->kx_mq)) { receiver->kx_mq = NULL; GNUNET_MQ_destroy (mq); - } + }*/ if (NULL != receiver->kx_qh) { GNUNET_TRANSPORT_communicator_mq_del (receiver->kx_qh); receiver->kx_qh = NULL; + receiver->kx_mq = NULL; } - if (NULL != (mq = receiver->d_mq)) + /*if (NULL != (mq = receiver->d_mq)) { receiver->d_mq = NULL; GNUNET_MQ_destroy (mq); - } + }*/ if (NULL != receiver->d_qh) { GNUNET_TRANSPORT_communicator_mq_del (receiver->d_qh); @@ -956,8 +1047,9 @@ kce_generate (struct SharedSecret *ss, uint32_t seq) * Destroy @a ss and associated key cache entries. * * @param ss shared secret to destroy + * @param withoutKce If GNUNET_YES shared secrets with kce will not be destroyed. */ -static void +static int secret_destroy (struct SharedSecret *ss, int withoutKce) { struct SenderAddress *sender; @@ -970,7 +1062,7 @@ secret_destroy (struct SharedSecret *ss, int withoutKce) ss->sequence_allowed); if (withoutKce && (ss->sequence_allowed > 0)) - return; + return GNUNET_NO; if (NULL != (sender = ss->sender)) { @@ -993,6 +1085,7 @@ secret_destroy (struct SharedSecret *ss, int withoutKce) GNUNET_CONTAINER_multishortmap_size (key_cache), GNUNET_NO); GNUNET_free (ss); + return GNUNET_YES; } @@ -1005,6 +1098,7 @@ secret_destroy (struct SharedSecret *ss, int withoutKce) static void sender_destroy (struct SenderAddress *sender) { + sender->sender_destroy_called = GNUNET_YES; GNUNET_assert ( GNUNET_YES == GNUNET_CONTAINER_multipeermap_remove (senders, &sender->target, sender)); @@ -1100,20 +1194,26 @@ check_timeouts (void *cls) rt = GNUNET_TIME_UNIT_FOREVER_REL; while (NULL != (receiver = GNUNET_CONTAINER_heap_peek (receivers_heap))) { - rt = GNUNET_TIME_absolute_get_remaining (receiver->timeout); - if (0 != rt.rel_value_us) - break; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Receiver timed out\n"); - receiver_destroy (receiver); + if (GNUNET_YES != receiver->receiver_destroy_called) + { + rt = GNUNET_TIME_absolute_get_remaining (receiver->timeout); + if (0 != rt.rel_value_us) + break; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiver timed out\n"); + receiver_destroy (receiver); + } } st = GNUNET_TIME_UNIT_FOREVER_REL; while (NULL != (sender = GNUNET_CONTAINER_heap_peek (senders_heap))) { - st = GNUNET_TIME_absolute_get_remaining (sender->timeout); - if (0 != st.rel_value_us) - break; - sender_destroy (sender); + if (GNUNET_YES != sender->sender_destroy_called) + { + st = GNUNET_TIME_absolute_get_remaining (sender->timeout); + if (0 != st.rel_value_us) + break; + sender_destroy (sender); + } } delay = GNUNET_TIME_relative_min (rt, st); if (delay.rel_value_us < GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) @@ -1289,7 +1389,7 @@ setup_shared_secret_dec (const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral) */ static struct SharedSecret * setup_shared_secret_enc (const struct GNUNET_CRYPTO_EcdhePrivateKey *ephemeral, - struct ReceiverAddress *receiver) + struct ReceiverAddress *receiver, int add_to_receiver) { struct SharedSecret *ss; @@ -1316,6 +1416,114 @@ setup_shared_secret_enc (const struct GNUNET_CRYPTO_EcdhePrivateKey *ephemeral, static void setup_receiver_mq (struct ReceiverAddress *receiver); +/** + * Destroying all secrets. Depending on parameter we keep those secrets having a kce. + * + * @param ss The secret we will not destroy. + * @param withoutKce If GNUNET_YES shared secrets with kce will not be destroyed. + */ +static void +destroy_all_secrets (struct SharedSecret *ss, int withoutKce) +{ + struct SenderAddress *sender; + struct ReceiverAddress *receiver; + struct SharedSecret *ss_to_destroy; + struct SharedSecret *ss_start; + struct SharedSecret *pos; + int at_least_one_destroyed = GNUNET_NO; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting destroy all.\n"); + + if (NULL != (sender = ss->sender)) + { + ss_start = sender->ss_head; + } + else if (NULL != (receiver = ss->receiver)) + { + ss_start = receiver->ss_head; + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Shared secret has no sender or receiver!\n"); + return; + } + + pos = ss_start; + while ( NULL != pos) + { + ss_to_destroy = pos; + pos = pos->next; + + if (ss != ss_to_destroy) + at_least_one_destroyed = secret_destroy (ss_to_destroy, withoutKce); + } + + if ((ss != ss_start) && ! at_least_one_destroyed) + { + destroy_all_secrets (ss_start, GNUNET_NO); + } + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Finished destroy all.\n"); +} + +static void +add_acks (struct SharedSecret *ss, int acks_to_add, int remove_from_receiver) +{ + + struct ReceiverAddress *receiver = ss->receiver; + + if (NULL == ss) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "secret NULL!\n"); + + if (NULL == receiver) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiver NULL!\n"); + if (NULL == receiver->d_qh) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Queue NULL!\n"); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Tell transport we have %u more acks!\n", + acks_to_add); + GNUNET_TRANSPORT_communicator_mq_update (ch, + receiver->d_qh, + acks_to_add, + 1); + // Until here for alternativ 1 + + /* move ss to head to avoid discarding it anytime soon! */ + if (remove_from_receiver) + GNUNET_CONTAINER_DLL_remove (receiver->ss_head, receiver->ss_tail, ss); + GNUNET_CONTAINER_DLL_insert (receiver->ss_head, receiver->ss_tail, ss); + destroy_all_secrets (ss, GNUNET_YES); + + + // Uncomment this for alternativ 2 of backchannel functionality + /*if (receiver->acks_available != ack->acks_available) + { + receiver->acks_available = ack->acks_available; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Tell transport we have different number of acks!\n"); + GNUNET_TRANSPORT_communicator_mq_update (ch, + receiver->d_qh, + receiver->acks_available, + 1); + }*/ + // Until here for alternativ 2 +} + +static void +add_acks_rekey (struct ReceiverAddress *receiver) +{ + add_acks (receiver->ss_rekey, receiver->ss_rekey->sequence_allowed - 3, + GNUNET_NO); + receiver->acks_available = receiver->ss_rekey->sequence_allowed; + receiver->ss_rekey = NULL; +} /** * We received an ACK for @a pid. Check if it is for @@ -1332,18 +1540,48 @@ handle_ack (void *cls, const struct GNUNET_PeerIdentity *pid, void *value) { const struct UDPAck *ack = cls; struct ReceiverAddress *receiver = value; - struct SharedSecret *pos; + int acks_to_add; + uint32_t allowed; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "in handle ack\n"); - struct SharedSecret *ss_to_destroy; + + if (NULL != receiver->ss_rekey) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received secret with cmac %s \n", + GNUNET_h2s (&receiver->ss_rekey->cmac)); + + if ((NULL != receiver->ss_rekey) && (0 == memcmp (&ack->cmac, + &receiver->ss_rekey->cmac, + sizeof(struct + GNUNET_HashCode))) ) + { + allowed = ntohl (ack->sequence_max); + + if (allowed > receiver->ss_rekey->sequence_allowed) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%u > %u (%u %u) for rekey secrect %s\n", allowed, + receiver->ss_rekey->sequence_allowed, + receiver->acks_available, + ack->acks_available, + GNUNET_h2s (&receiver->ss_rekey->master)); + + receiver->ss_rekey->sequence_allowed = allowed; + + if (GNUNET_NO == receiver->rekeying) + add_acks_rekey (receiver); + + return GNUNET_NO; + } + } (void) pid; for (struct SharedSecret *ss = receiver->ss_head; NULL != ss; ss = ss->next) { if (0 == memcmp (&ack->cmac, &ss->cmac, sizeof(struct GNUNET_HashCode))) { - uint32_t allowed; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Found matching mac\n"); @@ -1358,41 +1596,19 @@ handle_ack (void *cls, const struct GNUNET_PeerIdentity *pid, void *value) ack->acks_available, GNUNET_h2s (&ss->master)); // Uncomment this for alternativ 1 of backchannel functionality + acks_to_add = (allowed - ss->sequence_allowed); + if ((GNUNET_NO == receiver->rekeying) && + (receiver->number_rekeying_kce < + 3) ) + { + acks_to_add -= (3 - receiver->number_rekeying_kce); + receiver->number_rekeying_kce = 3; + } receiver->acks_available += (allowed - ss->sequence_allowed); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Tell transport we have more acks!\n"); - GNUNET_TRANSPORT_communicator_mq_update (ch, - receiver->d_qh, - (allowed - - ss->sequence_allowed), - 1); - // Until here for alternativ 1 ss->sequence_allowed = allowed; - /* move ss to head to avoid discarding it anytime soon! */ - GNUNET_CONTAINER_DLL_remove (receiver->ss_head, receiver->ss_tail, ss); - GNUNET_CONTAINER_DLL_insert (receiver->ss_head, receiver->ss_tail, ss); - pos = receiver->ss_head; - while ( NULL != pos) - { - ss_to_destroy = pos; - pos = pos->next; - secret_destroy (ss_to_destroy, GNUNET_YES); - } + add_acks (ss, acks_to_add, GNUNET_YES); } - - // Uncomment this for alternativ 2 of backchannel functionality - /*if (receiver->acks_available != ack->acks_available) - { - receiver->acks_available = ack->acks_available; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Tell transport we have different number of acks!\n"); - GNUNET_TRANSPORT_communicator_mq_update (ch, - receiver->d_qh, - receiver->acks_available, - 1); - }*/ - // Until here for alternativ 2 return GNUNET_NO; } } @@ -1446,6 +1662,8 @@ try_handle_plaintext (struct SenderAddress *sender, } } + + static void kce_generate_cb (void *cls) { @@ -1477,6 +1695,30 @@ kce_generate_cb (void *cls) } +static void +kce_generate_rekey_cb (void *cls) +{ + struct SharedSecret *ss = cls; + + if (NULL == kce_task) + { + for (int i = 0; i < GENERATE_AT_ONCE; i++) + kce_generate (ss, ++ss->sequence_allowed); + + kce_task = GNUNET_SCHEDULER_add_delayed (WORKING_QUEUE_INTERVALL, + kce_generate_cb, + ss); + kce_task_rekey = NULL; + } + else + { + kce_task_rekey = GNUNET_SCHEDULER_add_delayed (WORKING_QUEUE_INTERVALL, + kce_generate_rekey_cb, + ss); + } +} + + /** * We established a shared secret with a sender. We should try to send * the sender an `struct UDPAck` at the next opportunity to allow the @@ -1489,9 +1731,6 @@ kce_generate_cb (void *cls) static void consider_ss_ack (struct SharedSecret *ss, int initial) { - struct SharedSecret *ss_to_destroy; - struct SharedSecret *pos; - GNUNET_assert (NULL != ss->sender); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Considering SS UDPAck %s\n", @@ -1535,18 +1774,13 @@ consider_ss_ack (struct SharedSecret *ss, int initial) &ss_finished->sender->target, COMMUNICATOR_ADDRESS_PREFIX, &ack.header); - pos = ss->sender->ss_head; - while ( NULL != pos) - { - ss_to_destroy = pos; - pos = pos->next; - secret_destroy (ss_to_destroy, GNUNET_YES); - } + destroy_all_secrets (ss, GNUNET_YES); kce_task = NULL; + kce_task_finished = GNUNET_NO; } - else if (((NULL == kce_task) && (KCN_THRESHOLD > - ss->sender->acks_available)) || - (ss->sender->num_secrets > MAX_SECRETS) ) + else if ((NULL == kce_task) && ((KCN_THRESHOLD > ss->sender->acks_available)|| + (GNUNET_YES == ss->sender->rekeying) || + (ss->sender->num_secrets > MAX_SECRETS) )) { // kce_generate (ss, ++ss->sequence_allowed); @@ -1556,39 +1790,13 @@ consider_ss_ack (struct SharedSecret *ss, int initial) ss); } - - - /*if (ss->active_kce_count < KCN_THRESHOLD) + else if ((NULL == kce_task_rekey) && (GNUNET_YES == + ss->sender->rekeying) ) { - struct UDPAck ack; - - /** - * If we previously have seen this ss - * we now generate KCN_TARGET KCEs. - * For the initial KX (active_kce_count==0), - * we only generate a single KCE to prevent - * unnecessary overhead. - - if (0 < ss->active_kce_count) - { - while (ss->active_kce_count < KCN_TARGET) - kce_generate (ss, ++ss->sequence_allowed); - } - else { - kce_generate (ss, ++ss->sequence_allowed); - } - ack.header.type = htons (GNUNET_MESSAGE_TYPE_COMMUNICATOR_UDP_ACK); - ack.header.size = htons (sizeof(ack)); - ack.sequence_max = htonl (ss->sequence_allowed); - ack.cmac = ss->cmac; - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Notifying transport of UDPAck %s\n", - GNUNET_i2s_full (&ss->sender->target)); - GNUNET_TRANSPORT_communicator_notify (ch, - &ss->sender->target, - COMMUNICATOR_ADDRESS_PREFIX, - &ack.header); - }*/ + kce_task_rekey = GNUNET_SCHEDULER_add_delayed (WORKING_QUEUE_INTERVALL, + kce_generate_rekey_cb, + ss); + } } @@ -1631,9 +1839,107 @@ decrypt_box (const struct UDPBox *box, "decrypted UDPBox with kid %s\n", GNUNET_sh2s (&box->kid)); try_handle_plaintext (ss->sender, out_buf, sizeof(out_buf)); - consider_ss_ack (ss, GNUNET_NO); + if ((GNUNET_NO == box->rekeying) && (GNUNET_YES == ss->sender->rekeying)) + { + ss->sender->rekeying = GNUNET_NO; + // destroy_all_secrets (ss, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiver stopped rekeying.\n"); + } + else if (GNUNET_NO == box->rekeying) + consider_ss_ack (ss, GNUNET_YES); + else{ + ss->sender->rekeying = GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiver started rekeying.\n"); + } } +/** + * We received a @a rekey with matching @a kce. Decrypt and process it. + * + * @param rekey the data we received + * @param rekey_len number of bytes in @a rekey + * @param kce key index to decrypt @a rekey + */ +static void +decrypt_rekey (const struct UDPRekey *rekey, + size_t rekey_len, + struct KeyCacheEntry *kce, + struct SenderAddress *sender) +{ + struct SharedSecret *ss = kce->ss; + struct SharedSecret *ss_rekey; + char out_buf[rekey_len - sizeof(*rekey)]; + struct GNUNET_HashCode *master; + + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "decrypt_rekey.\n"); + + GNUNET_assert (NULL != ss->sender); + if (GNUNET_OK != try_decrypt (ss, + rekey->gcm_tag, + kce->sequence_number, + (const char *) &rekey[1], + sizeof(out_buf), + out_buf)) + { + GNUNET_STATISTICS_update (stats, + "# Decryption failures with valid KCE", + 1, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Decryption with kid %s failed\n", + GNUNET_sh2s (&rekey->kid)); + kce_destroy (kce); + return; + } + kce_destroy (kce); + GNUNET_STATISTICS_update (stats, + "# bytes decrypted with Rekey", + sizeof(out_buf), + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "decrypted UDPRekey with kid %s\n", + GNUNET_sh2s (&rekey->kid)); + /*cmac = (struct GNUNET_HashCode *) out_buf; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received secret with cmac %s \n", + GNUNET_h2s (&cmac));*/ + // ss_rekey = (struct SharedSecret *) out_buf; + master = (struct GNUNET_HashCode *) out_buf; + ss_rekey = GNUNET_new (struct SharedSecret); + ss_rekey->master = *master; + calculate_cmac (ss_rekey); + ss_rekey->sender = sender; + // ss_rekey->sequence_used = 0; + // ss_rekey->sequence_allowed = 0; + /* ss_rekey->active_kce_count = 0; */ + /* ss_rekey->prev = NULL; */ + /* ss_rekey->next = NULL; */ + /* GNUNET_assert (ss_rekey->prev == NULL && sender->ss_head != ss_rekey); */ + /* GNUNET_assert (ss_rekey->next == NULL && sender->ss_tail != ss_rekey); */ + GNUNET_CONTAINER_DLL_insert (sender->ss_head, sender->ss_tail, ss_rekey); + sender->num_secrets++; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received secret with cmac %s\n", + GNUNET_h2s (&(ss_rekey->cmac))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Received secret with master %s.\n", + GNUNET_h2s (&(ss_rekey->master))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "We have %u sequence_allowed.\n", + ss_rekey->sequence_allowed); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "We have a sender %p\n", + ss_rekey->sender); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "We have %u acks available.\n", + ss_rekey->sender->acks_available); + consider_ss_ack (ss_rekey, GNUNET_YES); + +} /** * Closure for #find_sender_by_address() @@ -1832,6 +2138,38 @@ sock_read (void *cls) } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Read %lu bytes\n", rcvd); + + if (rcvd > sizeof(struct UDPRekey)) + { + const struct UDPRekey *rekey; + const struct UDPBox *box; + struct KeyCacheEntry *kce; + struct SenderAddress *sender; + int do_decrypt = GNUNET_NO; + + rekey = (const struct UDPRekey *) buf; + box = (const struct UDPBox *) buf; + kce = GNUNET_CONTAINER_multishortmap_get (key_cache, &rekey->kid); + + if ((GNUNET_YES == box->rekeying)||(GNUNET_NO == box->rekeying)) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "UDPRekey has rekeying %u\n", + box->rekeying); + else + do_decrypt = GNUNET_YES; + if ((GNUNET_YES == do_decrypt)&& (NULL != kce) && (GNUNET_YES == + kce->ss->sender->rekeying)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "UDPRekey with kid %s\n", + GNUNET_sh2s (&rekey->kid)); + sender = setup_sender (&rekey->sender, (const struct sockaddr *) &sa, + salen); + decrypt_rekey (rekey, (size_t) rcvd, kce, sender); + return; + } + } + /* first, see if it is a UDPBox */ if (rcvd > sizeof(struct UDPBox)) { @@ -1949,7 +2287,20 @@ sock_read (void *cls) 1, GNUNET_NO); try_handle_plaintext (sender, &uc[1], sizeof(pbuf) - sizeof(*uc)); - consider_ss_ack (ss, GNUNET_YES); + if ((GNUNET_NO == kx->rekeying) && (GNUNET_YES == ss->sender->rekeying)) + { + ss->sender->rekeying = GNUNET_NO; + // destroy_all_secrets (ss, GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiver stopped rekeying.\n"); + } + else if (GNUNET_NO == kx->rekeying) + consider_ss_ack (ss, GNUNET_YES); + else{ + ss->sender->rekeying = GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Receiver started rekeying.\n"); + } /*if (sender->num_secrets > MAX_SECRETS) secret_destroy (sender->ss_tail);*/ } @@ -2138,26 +2489,13 @@ mq_send_kx (struct GNUNET_MQ_Handle *mq, size_t dpos; gcry_cipher_hd_t out_cipher; struct SharedSecret *ss; - struct SharedSecret *ss_to_destroy; - struct SharedSecret *pos; - - if (receiver->num_secrets > MAX_SECRETS) - { - pos = receiver->ss_head; - while ( NULL != pos) - { - ss_to_destroy = pos; - pos = pos->next; - secret_destroy (ss_to_destroy, GNUNET_YES); - } - } - GNUNET_assert (mq == receiver->kx_mq); if (msize > receiver->kx_mtu) { GNUNET_break (0); - receiver_destroy (receiver); + if (GNUNET_YES != receiver->receiver_destroy_called) + receiver_destroy (receiver); return; } reschedule_receiver_timeout (receiver); @@ -2165,7 +2503,13 @@ mq_send_kx (struct GNUNET_MQ_Handle *mq, /* setup key material */ GNUNET_CRYPTO_ecdhe_key_create (&epriv); - ss = setup_shared_secret_enc (&epriv, receiver); + ss = setup_shared_secret_enc (&epriv, receiver, GNUNET_YES); + + if (receiver->num_secrets > MAX_SECRETS) + { + destroy_all_secrets (ss, GNUNET_YES); + } + setup_cipher (&ss->master, 0, &out_cipher); /* compute 'uc' */ uc.sender = my_identity; @@ -2199,6 +2543,10 @@ mq_send_kx (struct GNUNET_MQ_Handle *mq, GNUNET_assert ( 0 == gcry_cipher_gettag (out_cipher, kx.gcm_tag, sizeof(kx.gcm_tag))); gcry_cipher_close (out_cipher); + if (GNUNET_NO == receiver->rekeying) + kx.rekeying = GNUNET_NO; + else + kx.rekeying = GNUNET_YES; memcpy (dgram, &kx, sizeof(kx)); if (-1 == GNUNET_NETWORK_socket_sendto (udp_sock, dgram, @@ -2212,6 +2560,162 @@ mq_send_kx (struct GNUNET_MQ_Handle *mq, GNUNET_MQ_impl_send_continue (mq); } +static void +check_for_rekeying (struct ReceiverAddress *receiver, struct UDPBox *box) +{ + + struct GNUNET_TIME_Relative rt; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Timeout is %lu\n.", + receiver->rekey_timeout.abs_value_us); + + if (0 == receiver->rekey_timeout.abs_value_us) + { + receiver->rekey_timeout = GNUNET_TIME_relative_to_absolute ( + rekey_interval); + } + else + { + rt = GNUNET_TIME_absolute_get_remaining (receiver->rekey_timeout); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Relative time is %lu and timeout is %lu\n.", + rt.rel_value_us, + receiver->rekey_timeout.abs_value_us); + + if ((0 == rt.rel_value_us)||(receiver->rekey_send_bytes > + REKEY_MAX_BYTES) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Bytes send %lu greater than %llu max bytes\n.", + receiver->rekey_send_bytes, + REKEY_MAX_BYTES); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Relative time is %lu and timeout is %lu\n.", + rt.rel_value_us, + receiver->rekey_timeout.abs_value_us); + + receiver->rekey_timeout.abs_value_us = 0; + receiver->rekey_send_bytes = 0; + receiver->ss_rekey = NULL; + // destroy_all_secrets (ss, GNUNET_NO); + receiver->rekeying = GNUNET_YES; + receiver->rekey_acks_available = receiver->acks_available; + box->rekeying = GNUNET_YES; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sender started rekeying.\n"); + if (GNUNET_YES == box->rekeying) + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending rekeying with kid %s\n", + GNUNET_sh2s (&box->kid)); + } + } +} + +static void +send_UDPRekey (struct ReceiverAddress *receiver, struct SharedSecret *ss) +{ + uint8_t is_ss_rekey_sequence_allowed_zero = GNUNET_NO; + uint8_t is_acks_available_below = GNUNET_NO; + uint8_t send_rekey = GNUNET_NO; + uint16_t not_below; + struct GNUNET_CRYPTO_EcdhePrivateKey epriv; + struct UDPRekey *rekey; + size_t dpos; + + char rekey_dgram[sizeof(struct UDPRekey) + receiver->d_mtu]; + + if (NULL != receiver->ss_rekey) + { + not_below = (receiver->rekey_acks_available + - (receiver->rekey_acks_available % 3)) / 3; + is_ss_rekey_sequence_allowed_zero = (0 == + receiver->ss_rekey->sequence_allowed); + is_acks_available_below = (receiver->acks_available > not_below); + send_rekey = (0 == (receiver->acks_available - not_below) % not_below) && + is_acks_available_below && is_ss_rekey_sequence_allowed_zero; + } + else if (NULL == receiver->ss_rekey) + { + /* setup key material */ + GNUNET_CRYPTO_ecdhe_key_create (&epriv); + receiver->ss_rekey = setup_shared_secret_enc (&epriv, receiver, + GNUNET_NO); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Setup secret with cmac %s\n", + GNUNET_h2s (&(receiver->ss_rekey->cmac))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Setup secret with master %s.\n", + GNUNET_h2s (&(receiver->ss_rekey->master))); + } + + if (send_rekey) + { + GNUNET_assert (0 != receiver->number_rekeying_kce); + gcry_cipher_hd_t rekey_out_cipher; + + while (NULL != ss && ss->sequence_used >= ss->sequence_allowed) + { + ss = ss->prev; + } + + if (NULL != ss) + { + rekey = (struct UDPRekey *) rekey_dgram; + rekey->sender = my_identity; + ss->sequence_used++; + get_kid (&ss->master, ss->sequence_used, &rekey->kid); + receiver->number_rekeying_kce--; + setup_cipher (&ss->master, ss->sequence_used, &rekey_out_cipher); + /* Append encrypted payload to dgram */ + dpos = sizeof(struct UDPRekey); + + GNUNET_assert ( + 0 == gcry_cipher_encrypt (rekey_out_cipher, &rekey_dgram[dpos], + sizeof(receiver->ss_rekey->master), + &(receiver->ss_rekey->master), + sizeof(receiver->ss_rekey->master))); + dpos += sizeof(receiver->ss_rekey->master); + /* GNUNET_assert ( */ + /* 0 == gcry_cipher_encrypt (rekey_out_cipher, &rekey_dgram[dpos], */ + /* /\*sizeof(receiver->ss_rekey->cmac), */ + /* &(receiver->ss_rekey->cmac), */ + /* sizeof(receiver->ss_rekey->cmac))); */ + /* dpos += sizeof(receiver->ss_rekey->cmac);*\/ */ + /* sizeof(receiver->ss_rekey), */ + /* receiver->ss_rekey, */ + /* sizeof(receiver->ss_rekey))); */ + /* dpos += sizeof(receiver->ss_rekey); */ + do_pad (rekey_out_cipher, &rekey_dgram[dpos], sizeof(rekey_dgram) + - dpos); + GNUNET_assert (0 == gcry_cipher_gettag (rekey_out_cipher, + rekey->gcm_tag, + sizeof(rekey->gcm_tag))); + gcry_cipher_close (rekey_out_cipher); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending rekey with kid %s and master %s\n", + GNUNET_sh2s (&rekey->kid), + GNUNET_h2s (&(receiver->ss_rekey->master))); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending rekey with cmac %s\n", + GNUNET_h2s (&(receiver->ss_rekey->cmac))); + + if (-1 == GNUNET_NETWORK_socket_sendto (udp_sock, + rekey_dgram, + sizeof(rekey_dgram), + receiver->address, + receiver->address_len)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "send"); + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending UDPRekey to %s\n", GNUNET_a2s (receiver->address, + receiver-> + address_len)); + } + } +} + /** * Signature of functions implementing the sending functionality of a @@ -2228,15 +2732,14 @@ mq_send_d (struct GNUNET_MQ_Handle *mq, { struct ReceiverAddress *receiver = impl_state; uint16_t msize = ntohs (msg->size); - struct GNUNET_TIME_Relative rt; - struct SharedSecret *pos; GNUNET_assert (mq == receiver->d_mq); if ((msize > receiver->d_mtu) || (0 == receiver->acks_available)) { GNUNET_break (0); - receiver_destroy (receiver); + if (GNUNET_YES != receiver->receiver_destroy_called) + receiver_destroy (receiver); return; } reschedule_receiver_timeout (receiver); @@ -2278,6 +2781,16 @@ mq_send_d (struct GNUNET_MQ_Handle *mq, box->gcm_tag, sizeof(box->gcm_tag))); gcry_cipher_close (out_cipher); + + receiver->rekey_send_bytes += sizeof(struct UDPBox) + receiver->d_mtu; + + if (GNUNET_NO == receiver->rekeying) + box->rekeying = GNUNET_NO; + else + box->rekeying = GNUNET_YES; + + check_for_rekeying (receiver, box); + if (-1 == GNUNET_NETWORK_socket_sendto (udp_sock, dgram, sizeof(dgram), @@ -2288,38 +2801,26 @@ mq_send_d (struct GNUNET_MQ_Handle *mq, "Sending UDPBox to %s\n", GNUNET_a2s (receiver->address, receiver->address_len)); GNUNET_MQ_impl_send_continue (mq); - // receiver->acks_available--; + receiver->acks_available--; if (0 == receiver->acks_available) { /* We have no more ACKs */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No more acks\n"); - } - - /* (NULL == rekey_timeout) - rekey_timeout = GNUNET_TIME_relative_to_absolute (REKEY_TIME_INTERVAL); - else - { - rt = GNUNET_TIME_absolute_get_remaining (rekey_timeout); - if (0 == rt.rel_value_us) + if (GNUNET_YES == receiver->rekeying) { - rekey_timeout = NULL; - pos = receiver->ss_head; - while ( NULL != pos) - { - ss_to_destroy = pos; - pos = pos->next; - secret_destroy (ss_to_destroy, GNUNET_NO); - } - if (0 != receiver->acks_available) - GNUNET_TRANSPORT_communicator_mq_update (ch, - receiver->d_qh, - // TODO We can not do this. But how can we signal this queue is not able to handle a message. Test code interprets q-len as additional length. - -receiver->acks_available, - 1); - } - }*/ + receiver->rekeying = GNUNET_NO; + if ((NULL != receiver->ss_rekey) && (0 < + receiver->ss_rekey-> + sequence_allowed) ) + add_acks_rekey (receiver); + } + } + else if ((GNUNET_YES == receiver->rekeying) ) + { + send_UDPRekey (receiver, ss); + } return; } @@ -2343,7 +2844,8 @@ mq_destroy_d (struct GNUNET_MQ_Handle *mq, void *impl_state) if (mq == receiver->d_mq) { receiver->d_mq = NULL; - receiver_destroy (receiver); + if (GNUNET_YES != receiver->receiver_destroy_called) + receiver_destroy (receiver); } } @@ -2365,7 +2867,8 @@ mq_destroy_kx (struct GNUNET_MQ_Handle *mq, void *impl_state) if (mq == receiver->kx_mq) { receiver->kx_mq = NULL; - receiver_destroy (receiver); + if (GNUNET_YES != receiver->receiver_destroy_called) + receiver_destroy (receiver); } } @@ -2997,6 +3500,13 @@ run (void *cls, return; } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_time (cfg, + COMMUNICATOR_CONFIG_SECTION, + "REKEY_INTERVAL", + &rekey_interval)) + rekey_interval = DEFAULT_REKEY_TIME_INTERVAL; + in = udp_address_to_sockaddr (bindto, &in_len); if (NULL == in) { diff --git a/src/transport/test_communicator_basic.c b/src/transport/test_communicator_basic.c index 8251a5169..df3c73b20 100644 --- a/src/transport/test_communicator_basic.c +++ b/src/transport/test_communicator_basic.c @@ -31,6 +31,7 @@ #include "gnunet_signatures.h" #include "gnunet_testing_lib.h" #include "transport.h" +#include "gnunet_statistics_service.h" #include @@ -54,6 +55,8 @@ GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_hs[NUM_PEERS]; static struct GNUNET_CONFIGURATION_Handle *cfg_peers[NUM_PEERS]; +static struct GNUNET_STATISTICS_Handle *stats[NUM_PEERS]; + static char *cfg_peers_name[NUM_PEERS]; static int ret; @@ -68,6 +71,8 @@ static struct GNUNET_TIME_Absolute timeout; static struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *my_tc; +static char *test_name; + #define SHORT_MESSAGE_SIZE 128 #define LONG_MESSAGE_SIZE 32000 /* FIXME */ @@ -509,6 +514,16 @@ update_avg_latency (const char*payload) } +static int +process_statistics (void *cls, + const char *subsystem, + const char *name, + uint64_t value, + int is_persistent) +{ + return GNUNET_OK; +} + /** * @brief Handle an incoming message * @@ -645,6 +660,15 @@ incoming_message_cb (void *cls, short_test (NULL); break; } + /* if (("rekey" == test_name) || ("backchannel" == test_name)) */ + /* { */ + /* GNUNET_STATISTICS_get (stats[1], */ + /* "C-UDP", */ + /* "# bytes decrypted with Rekey", */ + /* NULL, */ + /* &process_statistics, */ + /* NULL); */ + /* } */ LOG (GNUNET_ERROR_TYPE_DEBUG, "Finished\n"); GNUNET_SCHEDULER_shutdown (); @@ -695,6 +719,12 @@ run (void *cls) &incoming_message_cb, &handle_backchannel_cb, cfg_peers_name[i]); /* cls */ + + /* if (("rekey" == test_name) || ("backchannel" == test_name)) */ + /* { */ + /* stats[i] = GNUNET_STATISTICS_create ("C-UDP", */ + /* cfg_peers[i]); */ + /* } */ } GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); @@ -708,7 +738,6 @@ main (int argc, struct GNUNET_CRYPTO_EddsaPrivateKey *private_key; char *communicator_name; char *test_mode; - char *test_name; char *cfg_peer; phase = TP_INIT; diff --git a/src/transport/test_communicator_udp_rekey_peer1.conf b/src/transport/test_communicator_udp_rekey_peer1.conf index e7161e488..8fede6f8c 100644 --- a/src/transport/test_communicator_udp_rekey_peer1.conf +++ b/src/transport/test_communicator_udp_rekey_peer1.conf @@ -36,4 +36,12 @@ DISABLE_V6 = YES REKEY_INTERVAL = 100ms [communicator-udp] +#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args +#PREFIX = valgrind --leak-check=full --track-origins=yes BINDTO = 60002 +DISABLE_V6 = YES +MAX_QUEUE_LENGTH=5000 +REKEY_INTERVAL = 1s + +[communicator-test] +BACKCHANNEL_ENABLED = YES diff --git a/src/transport/test_communicator_udp_rekey_peer2.conf b/src/transport/test_communicator_udp_rekey_peer2.conf index 8f175a405..f545ba07a 100644 --- a/src/transport/test_communicator_udp_rekey_peer2.conf +++ b/src/transport/test_communicator_udp_rekey_peer2.conf @@ -36,4 +36,12 @@ DISABLE_V6 = YES REKEY_INTERVAL = 100ms [communicator-udp] +#PREFIX = xterm -geometry 100x85 -T peer1 -e gdb --args +#PREFIX = valgrind --leak-check=full --track-origins=yes BINDTO = 60003 +DISABLE_V6 = YES +MAX_QUEUE_LENGTH=5000 +REKEY_INTERVAL = 1s + +[communicator-test] +BACKCHANNEL_ENABLED = YES -- cgit v1.2.3 From 430ed7f8b4b33295e84990ec97cbb71431cef923 Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Mon, 30 Nov 2020 13:19:49 +0900 Subject: -fix communicator cleanup --- po/POTFILES.in | 4 +--- src/transport/gnunet-communicator-udp.c | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/po/POTFILES.in b/po/POTFILES.in index 00bdd7cbc..b0371ba21 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -205,8 +205,8 @@ src/json/json_generator.c src/json/json_helper.c src/json/json_mhd.c src/messenger/gnunet-messenger.c -src/messenger/gnunet-service-messenger.c src/messenger/gnunet-service-messenger_basement.c +src/messenger/gnunet-service-messenger.c src/messenger/gnunet-service-messenger_contact.c src/messenger/gnunet-service-messenger_handle.c src/messenger/gnunet-service-messenger_list_handles.c @@ -545,7 +545,5 @@ src/vpn/vpn_api.c src/zonemaster/gnunet-service-zonemaster.c src/zonemaster/gnunet-service-zonemaster-monitor.c src/fs/fs_api.h -src/include/gnunet_identity_service.h -src/include/gnunet_messenger_service.h src/testbed/testbed_api.h src/testbed/testbed_api_operations.h diff --git a/src/transport/gnunet-communicator-udp.c b/src/transport/gnunet-communicator-udp.c index 849adf4ab..073b1ea02 100644 --- a/src/transport/gnunet-communicator-udp.c +++ b/src/transport/gnunet-communicator-udp.c @@ -1668,8 +1668,7 @@ static void kce_generate_cb (void *cls) { struct SharedSecret *ss = cls; - - + kce_task = NULL; if (ss->sender->acks_available < KCN_TARGET) { @@ -1699,6 +1698,7 @@ static void kce_generate_rekey_cb (void *cls) { struct SharedSecret *ss = cls; + kce_task_rekey = NULL; if (NULL == kce_task) { @@ -3132,6 +3132,21 @@ do_shutdown (void *cls) GNUNET_SCHEDULER_cancel (broadcast_task); broadcast_task = NULL; } + if (NULL != kce_task_rekey) + { + GNUNET_SCHEDULER_cancel (kce_task_rekey); + kce_task_rekey = NULL; + } + if (NULL != kce_task) + { + GNUNET_SCHEDULER_cancel (kce_task); + kce_task = NULL; + } + if (NULL != timeout_task) + { + GNUNET_SCHEDULER_cancel (timeout_task); + timeout_task = NULL; + } if (NULL != read_task) { GNUNET_SCHEDULER_cancel (read_task); -- cgit v1.2.3 From 948a62546c5c772d89efd5268393464c95eb7f1e Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Mon, 30 Nov 2020 13:30:07 +0900 Subject: -rename communicator testing logic --- src/transport/Makefile.am | 2 +- src/transport/test_communicator_basic.c | 2 +- src/transport/transport-testing-communicator.c | 1164 ++++++++++++++++++++++++ src/transport/transport-testing-communicator.h | 355 ++++++++ src/transport/transport-testing2.c | 1164 ------------------------ src/transport/transport-testing2.h | 355 -------- 6 files changed, 1521 insertions(+), 1521 deletions(-) create mode 100644 src/transport/transport-testing-communicator.c create mode 100644 src/transport/transport-testing-communicator.h delete mode 100644 src/transport/transport-testing2.c delete mode 100644 src/transport/transport-testing2.h diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am index e0369059d..477973af9 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am @@ -168,7 +168,7 @@ libgnunettransporttesting_la_LDFLAGS = \ $(GN_LIB_LDFLAGS) libgnunettransporttesting2_la_SOURCES = \ - transport-testing2.c transport-testing2.h + transport-testing-communicator.c transport-testing-communicator.h libgnunettransporttesting2_la_LIBADD = \ libgnunettransport.la \ $(top_builddir)/src/hello/libgnunethello.la \ diff --git a/src/transport/test_communicator_basic.c b/src/transport/test_communicator_basic.c index df3c73b20..7ea04afdb 100644 --- a/src/transport/test_communicator_basic.c +++ b/src/transport/test_communicator_basic.c @@ -26,7 +26,7 @@ */ #include "platform.h" #include "gnunet_util_lib.h" -#include "transport-testing2.h" +#include "transport-testing-communicator.h" #include "gnunet_ats_transport_service.h" #include "gnunet_signatures.h" #include "gnunet_testing_lib.h" diff --git a/src/transport/transport-testing-communicator.c b/src/transport/transport-testing-communicator.c new file mode 100644 index 000000000..6d74b12e8 --- /dev/null +++ b/src/transport/transport-testing-communicator.c @@ -0,0 +1,1164 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/transport-testing-communicator.c + * @brief functions related to testing-tng + * @author Christian Grothoff + * @author Julius Bünger + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_protocols.h" +#include "gnunet_constants.h" +#include "transport-testing-communicator.h" +#include "gnunet_ats_transport_service.h" +#include "gnunet_hello_lib.h" +#include "gnunet_signatures.h" +#include "transport.h" +#include + +#define LOG(kind, ...) GNUNET_log_from (kind, "transport-testing2", __VA_ARGS__) + +struct MyClient +{ + struct MyClient *prev; + struct MyClient *next; + /** + * @brief Handle to the client + */ + struct GNUNET_SERVICE_Client *client; + + /** + * @brief Handle to the client + */ + struct GNUNET_MQ_Handle *c_mq; + + /** + * The TCH + */ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc; + +}; + +/** + * @brief Queue of a communicator and some context + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue +{ + /** + * @brief Handle to the TransportCommunicator + */ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h; + + /** + * @brief Envelope to a message that requests the opening of the queue. + * + * If the client already requests queue(s), but the communicator is not yet + * connected, we cannot send the request to open the queue. Save it until the + * communicator becomes available and send it then. + */ + struct GNUNET_MQ_Envelope *open_queue_env; + + /** + * @brief Peer ID of the peer on the other side of the queue + */ + struct GNUNET_PeerIdentity peer_id; + + /** + * @brief Queue ID + */ + uint32_t qid; + + /** + * @brief Current message id + */ + uint64_t mid; + + /** + * An `enum GNUNET_NetworkType` in NBO. + */ + uint32_t nt; + + /** + * Maximum transmission unit. UINT32_MAX for unlimited. + */ + uint32_t mtu; + + /** + * Queue length. UINT64_MAX for unlimited. + */ + uint64_t q_len; + + /** + * Queue prio + */ + uint32_t priority; + + /** + * An `enum GNUNET_TRANSPORT_ConnectionStatus` in NBO. + */ + uint32_t cs; + + /** + * @brief Next element inside a DLL + */ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *next; + + /** + * @brief Previous element inside a DLL + */ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *prev; +}; + + +/** + * @brief Handle/Context to a single transmission + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorTransmission +{ +}; + + +/** + * @brief Check whether incoming msg indicating available communicator is + * correct + * + * @param cls Closure + * @param msg Message struct + * + * @return GNUNET_YES in case message is correct + */ +static int +check_communicator_available ( + void *cls, + const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *msg) +{ + uint16_t size; + + size = ntohs (msg->header.size) - sizeof(*msg); + if (0 == size) + return GNUNET_OK; /* receive-only communicator */ + GNUNET_MQ_check_zero_termination (msg); + return GNUNET_OK; +} + + +/** + * @brief Handle new communicator + * + * Store characteristics of communicator, call respective client callback. + * + * @param cls Closure - communicator handle + * @param msg Message struct + */ +static void +handle_communicator_available ( + void *cls, + const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + uint16_t size; + tc_h->c_mq = client->c_mq; + + size = ntohs (msg->header.size) - sizeof(*msg); + if (0 == size) + { + GNUNET_SERVICE_client_continue (client->client); + return; /* receive-only communicator */ + } + tc_h->c_characteristics = ntohl (msg->cc); + tc_h->c_addr_prefix = GNUNET_strdup ((const char *) &msg[1]); + if (NULL != tc_h->communicator_available_cb) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "calling communicator_available_cb()\n"); + tc_h->communicator_available_cb (tc_h->cb_cls, + tc_h, + tc_h->c_characteristics, + tc_h->c_addr_prefix); + } + GNUNET_SERVICE_client_continue (client->client); + LOG (GNUNET_ERROR_TYPE_DEBUG, "finished communicator_available_cb()\n"); + +} + + +/** + * Incoming message. Test message is well-formed. + * + * @param cls the client + * @param msg the send message that was sent + * @return #GNUNET_OK if message is well-formed + */ +static int +check_communicator_backchannel (void *cls, + const struct + GNUNET_TRANSPORT_CommunicatorBackchannel *msg) +{ + // struct TransportClient *tc = cls; + + // if (CT_COMMUNICATOR != tc->type) + // { + // GNUNET_break (0); + // return GNUNET_SYSERR; + // } + // GNUNET_MQ_check_boxed_message (msg); + return GNUNET_OK; +} + + +/** + * @brief Receive an incoming message. + * + * Pass the message to the client. + * + * @param cls Closure - communicator handle + * @param msg Message + */ +static void +handle_communicator_backchannel (void *cls, + const struct + GNUNET_TRANSPORT_CommunicatorBackchannel * + bc_msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *other_tc_h; + struct GNUNET_MessageHeader *msg; + msg = (struct GNUNET_MessageHeader *) &bc_msg[1]; + uint16_t isize = ntohs (msg->size); + const char *target_communicator = ((const char *) msg) + isize; + struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming *cbi; + struct GNUNET_MQ_Envelope *env; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received backchannel message\n"); + if (tc_h->bc_enabled != GNUNET_YES) + { + GNUNET_SERVICE_client_continue (client->client); + return; + } + /* Find client providing this communicator */ + /* Finally, deliver backchannel message to communicator */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Delivering backchannel message of type %u to %s\n", + ntohs (msg->type), + target_communicator); + other_tc_h = tc_h->bc_cb (tc_h, msg, (struct + GNUNET_PeerIdentity*) &bc_msg->pid); + env = GNUNET_MQ_msg_extra ( + cbi, + isize, + GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL_INCOMING); + cbi->pid = tc_h->peer_id; + memcpy (&cbi[1], msg, isize); + + + GNUNET_MQ_send (other_tc_h->c_mq, env); + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * Address of our peer added. Test message is well-formed. + * + * @param cls the client + * @param aam the send message that was sent + * @return #GNUNET_OK if message is well-formed + */ +static int +check_add_address (void *cls, + const struct GNUNET_TRANSPORT_AddAddressMessage *msg) +{ + // if (CT_COMMUNICATOR != tc->type) + // { + // GNUNET_break (0); + // return GNUNET_SYSERR; + // } + GNUNET_MQ_check_zero_termination (msg); + return GNUNET_OK; +} + + +/** + * @brief The communicator informs about an address. + * + * Store address and call client callback. + * + * @param cls Closure - communicator handle + * @param msg Message + */ +static void +handle_add_address (void *cls, + const struct GNUNET_TRANSPORT_AddAddressMessage *msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + uint16_t size; + size = ntohs (msg->header.size) - sizeof(*msg); + if (0 == size) + return; /* receive-only communicator */ + LOG (GNUNET_ERROR_TYPE_DEBUG, "received add address cb %u\n", size); + tc_h->c_address = GNUNET_strdup ((const char *) &msg[1]); + if (NULL != tc_h->add_address_cb) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, "calling add_address_cb()\n"); + tc_h->add_address_cb (tc_h->cb_cls, + tc_h, + tc_h->c_address, + GNUNET_TIME_relative_ntoh (msg->expiration), + msg->aid, + ntohl (msg->nt)); + } + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * Incoming message. Test message is well-formed. + * + * @param cls the client + * @param msg the send message that was sent + * @return #GNUNET_OK if message is well-formed + */ +static int +check_incoming_msg (void *cls, + const struct GNUNET_TRANSPORT_IncomingMessage *msg) +{ + // struct TransportClient *tc = cls; + + // if (CT_COMMUNICATOR != tc->type) + // { + // GNUNET_break (0); + // return GNUNET_SYSERR; + // } + GNUNET_MQ_check_boxed_message (msg); + return GNUNET_OK; +} + + +/** + * @brief Receive an incoming message. + * + * Pass the message to the client. + * + * @param cls Closure - communicator handle + * @param msg Message + */ +static void +handle_incoming_msg (void *cls, + const struct GNUNET_TRANSPORT_IncomingMessage *inc_msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + struct GNUNET_MessageHeader *msg; + msg = (struct GNUNET_MessageHeader *) &inc_msg[1]; + size_t payload_len = ntohs (msg->size) - sizeof (struct + GNUNET_MessageHeader); + if (NULL != tc_h->incoming_msg_cb) + { + tc_h->incoming_msg_cb (tc_h->cb_cls, + tc_h, + (char*) &msg[1], + payload_len); + } + else + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Incoming message from communicator but no handler!\n"); + } + if (GNUNET_YES == ntohl (inc_msg->fc_on)) + { + /* 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); + GNUNET_assert (NULL != env); + ack->reserved = htonl (0); + ack->fc_id = inc_msg->fc_id; + ack->sender = inc_msg->sender; + GNUNET_MQ_send (tc_h->c_mq, env); + } + + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * @brief Communicator informs that it tries to establish requested queue + * + * @param cls Closure - communicator handle + * @param msg Message + */ +static void +handle_queue_create_ok (void *cls, + const struct GNUNET_TRANSPORT_CreateQueueResponse *msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + + if (NULL != tc_h->queue_create_reply_cb) + { + tc_h->queue_create_reply_cb (tc_h->cb_cls, tc_h, GNUNET_YES); + } + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * @brief Communicator informs that it wont try establishing requested queue. + * + * It will not do so probably because the address is bougus (see comment to + * #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL) + * + * @param cls Closure - communicator handle + * @param msg Message + */ +static void +handle_queue_create_fail ( + void *cls, + const struct GNUNET_TRANSPORT_CreateQueueResponse *msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + + if (NULL != tc_h->queue_create_reply_cb) + { + tc_h->queue_create_reply_cb (tc_h->cb_cls, tc_h, GNUNET_NO); + } + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * New queue became available. Check message. + * + * @param cls the client + * @param aqm the send message that was sent + */ +static int +check_add_queue_message (void *cls, + const struct GNUNET_TRANSPORT_AddQueueMessage *aqm) +{ + GNUNET_MQ_check_zero_termination (aqm); + return GNUNET_OK; +} + + +/** + * @brief Handle new queue + * + * Store context and call client callback. + * + * @param cls Closure - communicator handle + * @param msg Message struct + */ +static void +handle_add_queue_message (void *cls, + const struct GNUNET_TRANSPORT_AddQueueMessage *msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Got queue with ID %u\n", msg->qid); + for (tc_queue = tc_h->queue_head; NULL != tc_queue; tc_queue = tc_queue->next) + { + if (tc_queue->qid == msg->qid) + break; + } + if (NULL == tc_queue) + { + tc_queue = + GNUNET_new (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue); + tc_queue->tc_h = tc_h; + tc_queue->qid = msg->qid; + tc_queue->peer_id = msg->receiver; + GNUNET_CONTAINER_DLL_insert (tc_h->queue_head, tc_h->queue_tail, tc_queue); + } + GNUNET_assert (tc_queue->qid == msg->qid); + GNUNET_assert (0 == GNUNET_memcmp (&tc_queue->peer_id, &msg->receiver)); + tc_queue->nt = msg->nt; + tc_queue->mtu = ntohl (msg->mtu); + tc_queue->cs = msg->cs; + tc_queue->priority = ntohl (msg->priority); + tc_queue->q_len = GNUNET_ntohll (msg->q_len); + if (NULL != tc_h->add_queue_cb) + { + tc_h->add_queue_cb (tc_h->cb_cls, tc_h, tc_queue, tc_queue->mtu); + } + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * @brief Handle new queue + * + * Store context and call client callback. + * + * @param cls Closure - communicator handle + * @param msg Message struct + */ +static void +handle_update_queue_message (void *cls, + const struct + GNUNET_TRANSPORT_UpdateQueueMessage *msg) +{ + struct MyClient *client = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = + client->tc; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Received queue update message for %u with q_len %" PRIu64 "\n", + msg->qid, GNUNET_ntohll (msg->q_len)); + tc_queue = tc_h->queue_head; + if (NULL != tc_queue) + { + while (tc_queue->qid != msg->qid) + { + tc_queue = tc_queue->next; + } + } + GNUNET_assert (tc_queue->qid == msg->qid); + GNUNET_assert (0 == GNUNET_memcmp (&tc_queue->peer_id, &msg->receiver)); + tc_queue->nt = msg->nt; + tc_queue->mtu = ntohl (msg->mtu); + tc_queue->cs = msg->cs; + tc_queue->priority = ntohl (msg->priority); + // Uncomment this for alternativ 1 of backchannel functionality + tc_queue->q_len += GNUNET_ntohll (msg->q_len); + // Until here for alternativ 1 + // Uncomment this for alternativ 2 of backchannel functionality + // tc_queue->q_len = GNUNET_ntohll (msg->q_len); + // Until here for alternativ 2 + GNUNET_SERVICE_client_continue (client->client); +} + + +/** + * @brief Shut down the service + * + * @param cls Closure - Handle to the service + */ +static void +shutdown_service (void *cls) +{ + struct GNUNET_SERVICE_Handle *h = cls; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Shutting down service!\n"); + + GNUNET_SERVICE_stop (h); +} + + +/** + * @brief Callback called when new Client (Communicator) connects + * + * @param cls Closure - TransporCommmunicator Handle + * @param client Client + * @param mq Messagequeue + * + * @return TransportCommunicator Handle + */ +static void * +connect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + struct GNUNET_MQ_Handle *mq) +{ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; + struct MyClient *new_c; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p connected to %p.\n", + client, tc_h); + new_c = GNUNET_new (struct MyClient); + new_c->client = client; + new_c->c_mq = mq; + new_c->tc = tc_h; + GNUNET_CONTAINER_DLL_insert (tc_h->client_head, + tc_h->client_tail, + new_c); + + if (NULL == tc_h->queue_head) + return new_c; + /* Iterate over queues. They are yet to be opened. Request opening. */ + for (struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue_iter = + tc_h->queue_head; + NULL != tc_queue_iter; + tc_queue_iter = tc_queue_iter->next) + { + if (NULL == tc_queue_iter->open_queue_env) + continue; + /* Send the previously created mq envelope to request the creation of the + * queue. */ + GNUNET_MQ_send (tc_h->c_mq, + tc_queue_iter->open_queue_env); + tc_queue_iter->open_queue_env = NULL; + } + return new_c; +} + + +/** + * @brief Callback called when Client disconnects + * + * @param cls Closure - TransportCommunicator Handle + * @param client Client + * @param internal_cls TransporCommmunicator Handle + */ +static void +disconnect_cb (void *cls, + struct GNUNET_SERVICE_Client *client, + void *internal_cls) +{ + struct MyClient *cl = cls; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; + + for (cl = tc_h->client_head; NULL != cl; cl = cl->next) + { + if (cl->client != client) + continue; + GNUNET_CONTAINER_DLL_remove (tc_h->client_head, + tc_h->client_tail, + cl); + if (cl->c_mq == tc_h->c_mq) + tc_h->c_mq = NULL; + GNUNET_free (cl); + break; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected.\n"); +} + + +/** + * Message was transmitted. Process the request. + * + * @param cls the client + * @param sma the send message that was sent + */ +static void +handle_send_message_ack (void *cls, + const struct GNUNET_TRANSPORT_SendMessageToAck *sma) +{ + struct MyClient *client = cls; + GNUNET_SERVICE_client_continue (client->client); + // NOP +} + + +/** + * @brief Start the communicator part of the transport service + * + * @param communicator_available Callback to be called when a new communicator + * becomes available + * @param cfg Configuration + */ +static void +transport_communicator_start ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) +{ + struct GNUNET_MQ_MessageHandler mh[] = { + GNUNET_MQ_hd_var_size (communicator_available, + GNUNET_MESSAGE_TYPE_TRANSPORT_NEW_COMMUNICATOR, + struct GNUNET_TRANSPORT_CommunicatorAvailableMessage, + tc_h), + GNUNET_MQ_hd_var_size (communicator_backchannel, + GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL, + struct GNUNET_TRANSPORT_CommunicatorBackchannel, + tc_h), + GNUNET_MQ_hd_var_size (add_address, + GNUNET_MESSAGE_TYPE_TRANSPORT_ADD_ADDRESS, + struct GNUNET_TRANSPORT_AddAddressMessage, + tc_h), + // GNUNET_MQ_hd_fixed_size (del_address, + // GNUNET_MESSAGE_TYPE_TRANSPORT_DEL_ADDRESS, + // struct GNUNET_TRANSPORT_DelAddressMessage, + // NULL), + GNUNET_MQ_hd_var_size (incoming_msg, + GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG, + struct GNUNET_TRANSPORT_IncomingMessage, + tc_h), + GNUNET_MQ_hd_fixed_size (queue_create_ok, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_OK, + struct GNUNET_TRANSPORT_CreateQueueResponse, + tc_h), + GNUNET_MQ_hd_fixed_size (queue_create_fail, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL, + struct GNUNET_TRANSPORT_CreateQueueResponse, + tc_h), + GNUNET_MQ_hd_var_size (add_queue_message, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP, + struct GNUNET_TRANSPORT_AddQueueMessage, + tc_h), + GNUNET_MQ_hd_fixed_size (update_queue_message, + GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_UPDATE, + struct GNUNET_TRANSPORT_UpdateQueueMessage, + tc_h), + // GNUNET_MQ_hd_fixed_size (del_queue_message, + // GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_TEARDOWN, + // struct GNUNET_TRANSPORT_DelQueueMessage, + // NULL), + GNUNET_MQ_hd_fixed_size (send_message_ack, + GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG_ACK, + struct GNUNET_TRANSPORT_SendMessageToAck, + tc_h), + GNUNET_MQ_handler_end () + }; + + + tc_h->sh = GNUNET_SERVICE_start ("transport", + tc_h->cfg, + &connect_cb, + &disconnect_cb, + tc_h, + mh); + GNUNET_assert (NULL != tc_h->sh); +} + + +/** + * @brief Task run at shutdown to kill communicator and clean up + * + * @param cls Closure - Process of communicator + */ +static void +shutdown_process (struct GNUNET_OS_Process *proc) +{ + if (0 != GNUNET_OS_process_kill (proc, SIGTERM)) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + "Error shutting down process with SIGERM, trying SIGKILL\n"); + if (0 != GNUNET_OS_process_kill (proc, SIGKILL)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Error shutting down process with SIGERM and SIGKILL\n"); + } + } + GNUNET_OS_process_destroy (proc); +} + +static void +shutdown_peerstore (void *cls) +{ + struct GNUNET_OS_Process *proc = cls; + shutdown_process (proc); +} + +static void +shutdown_communicator (void *cls) +{ + struct GNUNET_OS_Process *proc = cls; + shutdown_process (proc); +} + + +/** + * @brief Start the communicator + * + * @param cfgname Name of the communicator + */ +static void +communicator_start ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, + const char *binary_name) +{ + char *binary; + char *loprefix; + char *section_name; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "communicator_start\n"); + + section_name = strchr (binary_name, '-'); + section_name++; + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (tc_h->cfg, + section_name, + "PREFIX", + &loprefix)) + loprefix = GNUNET_strdup (""); + + + binary = GNUNET_OS_get_libexec_binary_path (binary_name); + tc_h->c_proc = GNUNET_OS_start_process_s (GNUNET_OS_INHERIT_STD_OUT_AND_ERR, + NULL, + loprefix, + binary, + binary_name, + "-c", + tc_h->cfg_filename, + NULL); + if (NULL == tc_h->c_proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start communicator!"); + return; + } + LOG (GNUNET_ERROR_TYPE_INFO, "started communicator\n"); + GNUNET_free (binary); +} + + +/** + * @brief Task run at shutdown to kill communicator and clean up + * + * @param cls Closure - Process of communicator + */ +static void +shutdown_nat (void *cls) +{ + struct GNUNET_OS_Process *proc = cls; + shutdown_process (proc); +} + + +/** + * @brief Task run at shutdown to kill the resolver process + * + * @param cls Closure - Process of communicator + */ +static void +shutdown_resolver (void *cls) +{ + struct GNUNET_OS_Process *proc = cls; + shutdown_process (proc); +} + + +static void +resolver_start (struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) +{ + char *binary; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "resolver_start\n"); + binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver"); + tc_h->resolver_proc = GNUNET_OS_start_process ( + GNUNET_OS_INHERIT_STD_OUT_AND_ERR + | GNUNET_OS_USE_PIPE_CONTROL, + NULL, + NULL, + NULL, + binary, + "gnunet-service-resolver", + "-c", + tc_h->cfg_filename, + NULL); + if (NULL == tc_h->resolver_proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start resolver service!"); + return; + } + LOG (GNUNET_ERROR_TYPE_INFO, "started resolver service\n"); + GNUNET_free (binary); + +} + + +/** + * @brief Start Peerstore + * + */ +static void +peerstore_start ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) +{ + char *binary; + + binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-peerstore"); + tc_h->ps_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR, + NULL, + NULL, + NULL, + binary, + "gnunet-service-peerstore", + "-c", + tc_h->cfg_filename, + NULL); + if (NULL == tc_h->ps_proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start Peerstore!"); + return; + } + LOG (GNUNET_ERROR_TYPE_INFO, "started Peerstore\n"); + GNUNET_free (binary); +} + +/** + * @brief Start NAT + * + */ +static void +nat_start ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) +{ + char *binary; + + LOG (GNUNET_ERROR_TYPE_DEBUG, "nat_start\n"); + binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-nat"); + tc_h->nat_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR + | GNUNET_OS_USE_PIPE_CONTROL, + NULL, + NULL, + NULL, + binary, + "gnunet-service-nat", + "-c", + tc_h->cfg_filename, + NULL); + if (NULL == tc_h->nat_proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start NAT!"); + return; + } + LOG (GNUNET_ERROR_TYPE_INFO, "started NAT\n"); + GNUNET_free (binary); +} + + +/** + * @brief Start communicator part of transport service and communicator + * + * @param service_name Name of the service + * @param cfg Configuration handle + * @param communicator_available_cb Callback that is called when a new + * @param add_address_cb Callback that is called when a new + * communicator becomes available + * @param cb_cls Closure to @a communicator_available_cb and @a + * + * @return Handle to the communicator duo + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * +GNUNET_TRANSPORT_TESTING_transport_communicator_service_start ( + const char *service_name, + const char *binary_name, + const char *cfg_filename, + const struct GNUNET_PeerIdentity *peer_id, + GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback + communicator_available_cb, + GNUNET_TRANSPORT_TESTING_AddAddressCallback add_address_cb, + GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback queue_create_reply_cb, + GNUNET_TRANSPORT_TESTING_AddQueueCallback add_queue_cb, + GNUNET_TRANSPORT_TESTING_IncomingMessageCallback incoming_message_cb, + GNUNET_TRANSPORT_TESTING_BackchannelCallback bc_cb, + void *cb_cls) +{ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Starting new transport/communicator combo with config %s\n", + cfg_filename); + tc_h = + GNUNET_new (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle); + tc_h->cfg_filename = GNUNET_strdup (cfg_filename); + tc_h->cfg = GNUNET_CONFIGURATION_create (); + if ((GNUNET_SYSERR == GNUNET_CONFIGURATION_load (tc_h->cfg, cfg_filename))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Malformed configuration file `%s', exit ...\n"), + cfg_filename); + GNUNET_free (tc_h->cfg_filename); + GNUNET_CONFIGURATION_destroy (tc_h->cfg); + GNUNET_free (tc_h); + return NULL; + } + tc_h->bc_enabled = GNUNET_CONFIGURATION_get_value_yesno (tc_h->cfg, + "communicator-test", + "BACKCHANNEL_ENABLED"); + tc_h->communicator_available_cb = communicator_available_cb; + tc_h->add_address_cb = add_address_cb; + tc_h->queue_create_reply_cb = queue_create_reply_cb; + tc_h->add_queue_cb = add_queue_cb; + tc_h->incoming_msg_cb = incoming_message_cb; + tc_h->bc_cb = bc_cb; + tc_h->peer_id = *peer_id; + tc_h->cb_cls = cb_cls; + + /* Start communicator part of service */ + transport_communicator_start (tc_h); + /* Start NAT */ + nat_start (tc_h); + /* Start resolver service */ + resolver_start (tc_h); + /* Start peerstore service */ + peerstore_start (tc_h); + /* Schedule start communicator */ + communicator_start (tc_h, + binary_name); + return tc_h; +} + + +void +GNUNET_TRANSPORT_TESTING_transport_communicator_service_stop ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) +{ + shutdown_communicator (tc_h->c_proc); + shutdown_service (tc_h->sh); + shutdown_nat (tc_h->nat_proc); + shutdown_resolver (tc_h->resolver_proc); + shutdown_peerstore (tc_h->ps_proc); + GNUNET_CONFIGURATION_destroy (tc_h->cfg); + GNUNET_free (tc_h); +} + + +/** + * @brief Instruct communicator to open a queue + * + * @param tc_h Handle to communicator which shall open queue + * @param peer_id Towards which peer + * @param address For which address + */ +void +GNUNET_TRANSPORT_TESTING_transport_communicator_open_queue ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, + const struct GNUNET_PeerIdentity *peer_id, + const char *address) +{ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; + static uint32_t idgen; + char *prefix; + struct GNUNET_TRANSPORT_CreateQueue *msg; + struct GNUNET_MQ_Envelope *env; + size_t alen; + + tc_queue = + GNUNET_new (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue); + tc_queue->tc_h = tc_h; + prefix = GNUNET_HELLO_address_to_prefix (address); + if (NULL == prefix) + { + GNUNET_break (0); /* We got an invalid address!? */ + GNUNET_free (tc_queue); + return; + } + GNUNET_free (prefix); + alen = strlen (address) + 1; + env = + GNUNET_MQ_msg_extra (msg, alen, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE); + msg->request_id = htonl (idgen++); + tc_queue->qid = msg->request_id; + msg->receiver = *peer_id; + tc_queue->peer_id = *peer_id; + memcpy (&msg[1], address, alen); + if (NULL != tc_h->c_mq) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Sending queue create immediately\n"); + GNUNET_MQ_send (tc_h->c_mq, env); + } + else + { + tc_queue->open_queue_env = env; + } + GNUNET_CONTAINER_DLL_insert (tc_h->queue_head, tc_h->queue_tail, tc_queue); +} + + +/** + * @brief Instruct communicator to send data + * + * @param tc_queue The queue to use for sending + * @param cont function to call when done sending + * @param cont_cls closure for @a cont + * @param payload Data to send + * @param payload_size Size of the @a payload + */ +void +GNUNET_TRANSPORT_TESTING_transport_communicator_send + (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, + GNUNET_SCHEDULER_TaskCallback cont, + void *cont_cls, + const void *payload, + size_t payload_size) +{ + struct GNUNET_MessageHeader *mh; + struct GNUNET_TRANSPORT_SendMessageTo *msg; + struct GNUNET_MQ_Envelope *env; + size_t inbox_size; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue_tmp; + + tc_queue = NULL; + for (tc_queue_tmp = tc_h->queue_head; + NULL != tc_queue_tmp; + tc_queue_tmp = tc_queue_tmp->next) + { + if (tc_queue_tmp->q_len <= 0) + continue; + if (NULL == tc_queue) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Selecting queue with prio %u, len %" PRIu64 " and MTU %u\n", + tc_queue_tmp->priority, + tc_queue_tmp->q_len, + tc_queue_tmp->mtu); + tc_queue = tc_queue_tmp; + continue; + } + if (tc_queue->priority < tc_queue_tmp->priority) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Selecting queue with prio %u, len %" PRIu64 " and MTU %u\n", + tc_queue_tmp->priority, + tc_queue_tmp->q_len, + tc_queue_tmp->mtu); + tc_queue = tc_queue_tmp; + } + } + GNUNET_assert (NULL != tc_queue); + // Uncomment this for alternativ 1 of backchannel functionality + if (tc_queue->q_len != GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED) + tc_queue->q_len--; + // Until here for alternativ 1 + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Sending message\n"); + inbox_size = sizeof (struct GNUNET_MessageHeader) + payload_size; + env = GNUNET_MQ_msg_extra (msg, + inbox_size, + GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG); + GNUNET_assert (NULL != env); + msg->qid = htonl (tc_queue->qid); + msg->mid = tc_queue->mid++; + msg->receiver = tc_queue->peer_id; + mh = (struct GNUNET_MessageHeader *) &msg[1]; + mh->size = htons (inbox_size); + mh->type = GNUNET_MESSAGE_TYPE_DUMMY; + memcpy (&mh[1], + payload, + payload_size); + if (NULL != cont) + GNUNET_MQ_notify_sent (env, + cont, + cont_cls); + GNUNET_MQ_send (tc_queue->tc_h->c_mq, + env); +} diff --git a/src/transport/transport-testing-communicator.h b/src/transport/transport-testing-communicator.h new file mode 100644 index 000000000..693e61515 --- /dev/null +++ b/src/transport/transport-testing-communicator.h @@ -0,0 +1,355 @@ +/* + This file is part of GNUnet. + Copyright (C) 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 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport/transport-testing-communicator.h + * @brief functions and structures related to testing-tng + * @author Christian Grothoff + * @author Julius Bünger + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_ats_transport_service.h" +#include "transport.h" + +/** + * @brief Queue of a communicator and some context + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue; + + +/** + * @brief Handle/Context to a single transmission + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorTransmission; + +/** + * @brief Function signature for callbacks that are called when new + * backchannel message arrived + * + * @param cls Closure + * @param msg Backchannel message + * @param pid Target peer + */ +typedef struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * +(*GNUNET_TRANSPORT_TESTING_BackchannelCallback)(void *cls, + struct GNUNET_MessageHeader *msg, + struct GNUNET_PeerIdentity *pid); + + +/** + * @brief Function signature for callbacks that are called when new + * communicators become available + * + * @param cls Closure + * @param tc_h Communicator handle + * @param cc Characteristics of communicator + * @param address_prefix Prefix of the address + */ +typedef void +(*GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback)(void *cls, + struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + enum + GNUNET_TRANSPORT_CommunicatorCharacteristics + cc, + char *address_prefix); + + +/** + * @brief Receive information about the address of a communicator. + * + * @param cls Closure + * @param tc_h Communicator handle + * @param address Address represented as string + * @param expiration Expiration + * @param aid Aid + * @param nt Network Type + */ +typedef void +(*GNUNET_TRANSPORT_TESTING_AddAddressCallback)(void *cls, + struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + const char *address, + struct GNUNET_TIME_Relative + expiration, + uint32_t aid, + enum GNUNET_NetworkType nt); + + +/** + * @brief Get informed about the success of a queue request. + * + * @param cls Closure + * @param tc_h Communicator handle + * @param will_try #GNUNET_YES if communicator will try to create queue + */ +typedef void +(*GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback)(void *cls, + struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + int will_try); + + +/** + * @brief Handle opening of queue + * + * @param cls Closure + * @param tc_h Communicator handle + * @param tc_queue Handle to newly opened queue + */ +typedef void +(*GNUNET_TRANSPORT_TESTING_AddQueueCallback)(void *cls, + struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue + *tc_queue, + size_t mtu); + + +/** + * @brief Handle an incoming message + * + * @param cls Closure + * @param tc_h Handle to the receiving communicator + * @param msg Received message + */ +typedef void +(*GNUNET_TRANSPORT_TESTING_IncomingMessageCallback)(void *cls, + struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + const char*payload, + size_t payload_len); + +/** + * @brief Handle to a transport communicator + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle +{ + /** + * Clients + */ + struct MyClient *client_head; + struct MyClient *client_tail; + + /** + * @brief Handle to the client + */ + struct GNUNET_MQ_Handle *c_mq; + + /** + * @brief Handle to the configuration + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * @brief File name of configuration file + */ + char *cfg_filename; + + struct GNUNET_PeerIdentity peer_id; + + /** + * @brief Handle to the transport service + */ + struct GNUNET_SERVICE_Handle *tsh; + + /** + * @brief Task that will be run on shutdown to stop and clean transport + * service + */ + struct GNUNET_SCHEDULER_Task *ts_shutdown_task; + + + /** + * @brief Process of the communicator + */ + struct GNUNET_OS_Process *c_proc; + + /** + * NAT process + */ + struct GNUNET_OS_Process *nat_proc; + + /** + * resolver service process + */ + struct GNUNET_OS_Process *resolver_proc; + + /** + * peerstore service process + */ + struct GNUNET_OS_Process *ps_proc; + + /** + * @brief Task that will be run on shutdown to stop and clean communicator + */ + struct GNUNET_SCHEDULER_Task *c_shutdown_task; + + /** + * @brief Characteristics of the communicator + */ + enum GNUNET_TRANSPORT_CommunicatorCharacteristics c_characteristics; + + /** + * @brief Specifies supported addresses + */ + char *c_addr_prefix; + + /** + * @brief Specifies supported addresses + */ + char *c_address; + + /** + * @brief Head of the DLL of queues associated with this communicator + */ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *queue_head; + + /** + * @brief Tail of the DLL of queues associated with this communicator + */ + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *queue_tail; + + /* Callbacks + Closures */ + /** + * @brief Callback called when a new communicator connects + */ + GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback + communicator_available_cb; + + /** + * @brief Callback called when a new communicator connects + */ + GNUNET_TRANSPORT_TESTING_AddAddressCallback add_address_cb; + + /** + * @brief Callback called when a new communicator connects + */ + GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback queue_create_reply_cb; + + /** + * @brief Callback called when a new communicator connects + */ + GNUNET_TRANSPORT_TESTING_AddQueueCallback add_queue_cb; + + /** + * @brief Callback called when a new communicator connects + */ + GNUNET_TRANSPORT_TESTING_IncomingMessageCallback incoming_msg_cb; + + /** + * @brief Backchannel callback + */ + GNUNET_TRANSPORT_TESTING_BackchannelCallback bc_cb; + + /** + * Our service handle + */ + struct GNUNET_SERVICE_Handle *sh; + + /** + * @brief Closure to the callback + */ + void *cb_cls; + + /** + * Backchannel supported + */ + int bc_enabled; +}; + +/** + * @brief Start communicator part of transport service and communicator + * + * @param service_name Name of the service + * @param cfg Configuration handle + * @param communicator_available Callback that is called when a new + * communicator becomes available + * @param add_address_cb Callback handling new addresses + * @param queue_create_reply_cb Callback handling success of queue requests + * @param add_queue_cb Callback handling freshly created queues + * @param incoming_message_cb Callback handling incoming messages + * @param cb_cls Closure to @p communicator_available + * + * @return Handle to the communicator duo + */ +struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * +GNUNET_TRANSPORT_TESTING_transport_communicator_service_start ( + const char *service_name, + const char *binary_name, + const char *cfg_filename, + const struct GNUNET_PeerIdentity *peer_id, + GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback + communicator_available_cb, + GNUNET_TRANSPORT_TESTING_AddAddressCallback add_address_cb, + GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback queue_create_reply_cb, + GNUNET_TRANSPORT_TESTING_AddQueueCallback add_queue_cb, + GNUNET_TRANSPORT_TESTING_IncomingMessageCallback incoming_message_cb, + GNUNET_TRANSPORT_TESTING_BackchannelCallback bc_cb, + void *cb_cls); + + +void +GNUNET_TRANSPORT_TESTING_transport_communicator_service_stop ( + struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h); + + +/** + * @brief Instruct communicator to open a queue + * + * @param tc_h Handle to communicator which shall open queue + * @param peer_id Towards which peer + * @param address For which address + */ +void +GNUNET_TRANSPORT_TESTING_transport_communicator_open_queue (struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + const struct + GNUNET_PeerIdentity + *peer_id, + const char *address); + + +/** + * @brief Instruct communicator to send data + * + * @param tc_queue The queue to use for sending + * @param cont function to call when done sending + * @param cont_cls closure for @a cont + * @param payload Data to send + * @param payload_size Size of the @a payload + */ +void +GNUNET_TRANSPORT_TESTING_transport_communicator_send (struct + GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle + *tc_h, + GNUNET_SCHEDULER_TaskCallback + cont, + void *cont_cls, + const void *payload, + size_t payload_size); diff --git a/src/transport/transport-testing2.c b/src/transport/transport-testing2.c deleted file mode 100644 index 076fbf331..000000000 --- a/src/transport/transport-testing2.c +++ /dev/null @@ -1,1164 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 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 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/transport-testing2.c - * @brief functions related to testing-tng - * @author Christian Grothoff - * @author Julius Bünger - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_protocols.h" -#include "gnunet_constants.h" -#include "transport-testing2.h" -#include "gnunet_ats_transport_service.h" -#include "gnunet_hello_lib.h" -#include "gnunet_signatures.h" -#include "transport.h" -#include - -#define LOG(kind, ...) GNUNET_log_from (kind, "transport-testing2", __VA_ARGS__) - -struct MyClient -{ - struct MyClient *prev; - struct MyClient *next; - /** - * @brief Handle to the client - */ - struct GNUNET_SERVICE_Client *client; - - /** - * @brief Handle to the client - */ - struct GNUNET_MQ_Handle *c_mq; - - /** - * The TCH - */ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc; - -}; - -/** - * @brief Queue of a communicator and some context - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue -{ - /** - * @brief Handle to the TransportCommunicator - */ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h; - - /** - * @brief Envelope to a message that requests the opening of the queue. - * - * If the client already requests queue(s), but the communicator is not yet - * connected, we cannot send the request to open the queue. Save it until the - * communicator becomes available and send it then. - */ - struct GNUNET_MQ_Envelope *open_queue_env; - - /** - * @brief Peer ID of the peer on the other side of the queue - */ - struct GNUNET_PeerIdentity peer_id; - - /** - * @brief Queue ID - */ - uint32_t qid; - - /** - * @brief Current message id - */ - uint64_t mid; - - /** - * An `enum GNUNET_NetworkType` in NBO. - */ - uint32_t nt; - - /** - * Maximum transmission unit. UINT32_MAX for unlimited. - */ - uint32_t mtu; - - /** - * Queue length. UINT64_MAX for unlimited. - */ - uint64_t q_len; - - /** - * Queue prio - */ - uint32_t priority; - - /** - * An `enum GNUNET_TRANSPORT_ConnectionStatus` in NBO. - */ - uint32_t cs; - - /** - * @brief Next element inside a DLL - */ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *next; - - /** - * @brief Previous element inside a DLL - */ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *prev; -}; - - -/** - * @brief Handle/Context to a single transmission - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorTransmission -{ -}; - - -/** - * @brief Check whether incoming msg indicating available communicator is - * correct - * - * @param cls Closure - * @param msg Message struct - * - * @return GNUNET_YES in case message is correct - */ -static int -check_communicator_available ( - void *cls, - const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *msg) -{ - uint16_t size; - - size = ntohs (msg->header.size) - sizeof(*msg); - if (0 == size) - return GNUNET_OK; /* receive-only communicator */ - GNUNET_MQ_check_zero_termination (msg); - return GNUNET_OK; -} - - -/** - * @brief Handle new communicator - * - * Store characteristics of communicator, call respective client callback. - * - * @param cls Closure - communicator handle - * @param msg Message struct - */ -static void -handle_communicator_available ( - void *cls, - const struct GNUNET_TRANSPORT_CommunicatorAvailableMessage *msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - uint16_t size; - tc_h->c_mq = client->c_mq; - - size = ntohs (msg->header.size) - sizeof(*msg); - if (0 == size) - { - GNUNET_SERVICE_client_continue (client->client); - return; /* receive-only communicator */ - } - tc_h->c_characteristics = ntohl (msg->cc); - tc_h->c_addr_prefix = GNUNET_strdup ((const char *) &msg[1]); - if (NULL != tc_h->communicator_available_cb) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "calling communicator_available_cb()\n"); - tc_h->communicator_available_cb (tc_h->cb_cls, - tc_h, - tc_h->c_characteristics, - tc_h->c_addr_prefix); - } - GNUNET_SERVICE_client_continue (client->client); - LOG (GNUNET_ERROR_TYPE_DEBUG, "finished communicator_available_cb()\n"); - -} - - -/** - * Incoming message. Test message is well-formed. - * - * @param cls the client - * @param msg the send message that was sent - * @return #GNUNET_OK if message is well-formed - */ -static int -check_communicator_backchannel (void *cls, - const struct - GNUNET_TRANSPORT_CommunicatorBackchannel *msg) -{ - // struct TransportClient *tc = cls; - - // if (CT_COMMUNICATOR != tc->type) - // { - // GNUNET_break (0); - // return GNUNET_SYSERR; - // } - // GNUNET_MQ_check_boxed_message (msg); - return GNUNET_OK; -} - - -/** - * @brief Receive an incoming message. - * - * Pass the message to the client. - * - * @param cls Closure - communicator handle - * @param msg Message - */ -static void -handle_communicator_backchannel (void *cls, - const struct - GNUNET_TRANSPORT_CommunicatorBackchannel * - bc_msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *other_tc_h; - struct GNUNET_MessageHeader *msg; - msg = (struct GNUNET_MessageHeader *) &bc_msg[1]; - uint16_t isize = ntohs (msg->size); - const char *target_communicator = ((const char *) msg) + isize; - struct GNUNET_TRANSPORT_CommunicatorBackchannelIncoming *cbi; - struct GNUNET_MQ_Envelope *env; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received backchannel message\n"); - if (tc_h->bc_enabled != GNUNET_YES) - { - GNUNET_SERVICE_client_continue (client->client); - return; - } - /* Find client providing this communicator */ - /* Finally, deliver backchannel message to communicator */ - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Delivering backchannel message of type %u to %s\n", - ntohs (msg->type), - target_communicator); - other_tc_h = tc_h->bc_cb (tc_h, msg, (struct - GNUNET_PeerIdentity*) &bc_msg->pid); - env = GNUNET_MQ_msg_extra ( - cbi, - isize, - GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL_INCOMING); - cbi->pid = tc_h->peer_id; - memcpy (&cbi[1], msg, isize); - - - GNUNET_MQ_send (other_tc_h->c_mq, env); - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * Address of our peer added. Test message is well-formed. - * - * @param cls the client - * @param aam the send message that was sent - * @return #GNUNET_OK if message is well-formed - */ -static int -check_add_address (void *cls, - const struct GNUNET_TRANSPORT_AddAddressMessage *msg) -{ - // if (CT_COMMUNICATOR != tc->type) - // { - // GNUNET_break (0); - // return GNUNET_SYSERR; - // } - GNUNET_MQ_check_zero_termination (msg); - return GNUNET_OK; -} - - -/** - * @brief The communicator informs about an address. - * - * Store address and call client callback. - * - * @param cls Closure - communicator handle - * @param msg Message - */ -static void -handle_add_address (void *cls, - const struct GNUNET_TRANSPORT_AddAddressMessage *msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - uint16_t size; - size = ntohs (msg->header.size) - sizeof(*msg); - if (0 == size) - return; /* receive-only communicator */ - LOG (GNUNET_ERROR_TYPE_DEBUG, "received add address cb %u\n", size); - tc_h->c_address = GNUNET_strdup ((const char *) &msg[1]); - if (NULL != tc_h->add_address_cb) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, "calling add_address_cb()\n"); - tc_h->add_address_cb (tc_h->cb_cls, - tc_h, - tc_h->c_address, - GNUNET_TIME_relative_ntoh (msg->expiration), - msg->aid, - ntohl (msg->nt)); - } - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * Incoming message. Test message is well-formed. - * - * @param cls the client - * @param msg the send message that was sent - * @return #GNUNET_OK if message is well-formed - */ -static int -check_incoming_msg (void *cls, - const struct GNUNET_TRANSPORT_IncomingMessage *msg) -{ - // struct TransportClient *tc = cls; - - // if (CT_COMMUNICATOR != tc->type) - // { - // GNUNET_break (0); - // return GNUNET_SYSERR; - // } - GNUNET_MQ_check_boxed_message (msg); - return GNUNET_OK; -} - - -/** - * @brief Receive an incoming message. - * - * Pass the message to the client. - * - * @param cls Closure - communicator handle - * @param msg Message - */ -static void -handle_incoming_msg (void *cls, - const struct GNUNET_TRANSPORT_IncomingMessage *inc_msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - struct GNUNET_MessageHeader *msg; - msg = (struct GNUNET_MessageHeader *) &inc_msg[1]; - size_t payload_len = ntohs (msg->size) - sizeof (struct - GNUNET_MessageHeader); - if (NULL != tc_h->incoming_msg_cb) - { - tc_h->incoming_msg_cb (tc_h->cb_cls, - tc_h, - (char*) &msg[1], - payload_len); - } - else - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "Incoming message from communicator but no handler!\n"); - } - if (GNUNET_YES == ntohl (inc_msg->fc_on)) - { - /* 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); - GNUNET_assert (NULL != env); - ack->reserved = htonl (0); - ack->fc_id = inc_msg->fc_id; - ack->sender = inc_msg->sender; - GNUNET_MQ_send (tc_h->c_mq, env); - } - - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * @brief Communicator informs that it tries to establish requested queue - * - * @param cls Closure - communicator handle - * @param msg Message - */ -static void -handle_queue_create_ok (void *cls, - const struct GNUNET_TRANSPORT_CreateQueueResponse *msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - - if (NULL != tc_h->queue_create_reply_cb) - { - tc_h->queue_create_reply_cb (tc_h->cb_cls, tc_h, GNUNET_YES); - } - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * @brief Communicator informs that it wont try establishing requested queue. - * - * It will not do so probably because the address is bougus (see comment to - * #GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL) - * - * @param cls Closure - communicator handle - * @param msg Message - */ -static void -handle_queue_create_fail ( - void *cls, - const struct GNUNET_TRANSPORT_CreateQueueResponse *msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - - if (NULL != tc_h->queue_create_reply_cb) - { - tc_h->queue_create_reply_cb (tc_h->cb_cls, tc_h, GNUNET_NO); - } - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * New queue became available. Check message. - * - * @param cls the client - * @param aqm the send message that was sent - */ -static int -check_add_queue_message (void *cls, - const struct GNUNET_TRANSPORT_AddQueueMessage *aqm) -{ - GNUNET_MQ_check_zero_termination (aqm); - return GNUNET_OK; -} - - -/** - * @brief Handle new queue - * - * Store context and call client callback. - * - * @param cls Closure - communicator handle - * @param msg Message struct - */ -static void -handle_add_queue_message (void *cls, - const struct GNUNET_TRANSPORT_AddQueueMessage *msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Got queue with ID %u\n", msg->qid); - for (tc_queue = tc_h->queue_head; NULL != tc_queue; tc_queue = tc_queue->next) - { - if (tc_queue->qid == msg->qid) - break; - } - if (NULL == tc_queue) - { - tc_queue = - GNUNET_new (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue); - tc_queue->tc_h = tc_h; - tc_queue->qid = msg->qid; - tc_queue->peer_id = msg->receiver; - GNUNET_CONTAINER_DLL_insert (tc_h->queue_head, tc_h->queue_tail, tc_queue); - } - GNUNET_assert (tc_queue->qid == msg->qid); - GNUNET_assert (0 == GNUNET_memcmp (&tc_queue->peer_id, &msg->receiver)); - tc_queue->nt = msg->nt; - tc_queue->mtu = ntohl (msg->mtu); - tc_queue->cs = msg->cs; - tc_queue->priority = ntohl (msg->priority); - tc_queue->q_len = GNUNET_ntohll (msg->q_len); - if (NULL != tc_h->add_queue_cb) - { - tc_h->add_queue_cb (tc_h->cb_cls, tc_h, tc_queue, tc_queue->mtu); - } - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * @brief Handle new queue - * - * Store context and call client callback. - * - * @param cls Closure - communicator handle - * @param msg Message struct - */ -static void -handle_update_queue_message (void *cls, - const struct - GNUNET_TRANSPORT_UpdateQueueMessage *msg) -{ - struct MyClient *client = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = - client->tc; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Received queue update message for %u with q_len %" PRIu64 "\n", - msg->qid, GNUNET_ntohll (msg->q_len)); - tc_queue = tc_h->queue_head; - if (NULL != tc_queue) - { - while (tc_queue->qid != msg->qid) - { - tc_queue = tc_queue->next; - } - } - GNUNET_assert (tc_queue->qid == msg->qid); - GNUNET_assert (0 == GNUNET_memcmp (&tc_queue->peer_id, &msg->receiver)); - tc_queue->nt = msg->nt; - tc_queue->mtu = ntohl (msg->mtu); - tc_queue->cs = msg->cs; - tc_queue->priority = ntohl (msg->priority); - // Uncomment this for alternativ 1 of backchannel functionality - tc_queue->q_len += GNUNET_ntohll (msg->q_len); - // Until here for alternativ 1 - // Uncomment this for alternativ 2 of backchannel functionality - // tc_queue->q_len = GNUNET_ntohll (msg->q_len); - // Until here for alternativ 2 - GNUNET_SERVICE_client_continue (client->client); -} - - -/** - * @brief Shut down the service - * - * @param cls Closure - Handle to the service - */ -static void -shutdown_service (void *cls) -{ - struct GNUNET_SERVICE_Handle *h = cls; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Shutting down service!\n"); - - GNUNET_SERVICE_stop (h); -} - - -/** - * @brief Callback called when new Client (Communicator) connects - * - * @param cls Closure - TransporCommmunicator Handle - * @param client Client - * @param mq Messagequeue - * - * @return TransportCommunicator Handle - */ -static void * -connect_cb (void *cls, - struct GNUNET_SERVICE_Client *client, - struct GNUNET_MQ_Handle *mq) -{ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; - struct MyClient *new_c; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "Client %p connected to %p.\n", - client, tc_h); - new_c = GNUNET_new (struct MyClient); - new_c->client = client; - new_c->c_mq = mq; - new_c->tc = tc_h; - GNUNET_CONTAINER_DLL_insert (tc_h->client_head, - tc_h->client_tail, - new_c); - - if (NULL == tc_h->queue_head) - return new_c; - /* Iterate over queues. They are yet to be opened. Request opening. */ - for (struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue_iter = - tc_h->queue_head; - NULL != tc_queue_iter; - tc_queue_iter = tc_queue_iter->next) - { - if (NULL == tc_queue_iter->open_queue_env) - continue; - /* Send the previously created mq envelope to request the creation of the - * queue. */ - GNUNET_MQ_send (tc_h->c_mq, - tc_queue_iter->open_queue_env); - tc_queue_iter->open_queue_env = NULL; - } - return new_c; -} - - -/** - * @brief Callback called when Client disconnects - * - * @param cls Closure - TransportCommunicator Handle - * @param client Client - * @param internal_cls TransporCommmunicator Handle - */ -static void -disconnect_cb (void *cls, - struct GNUNET_SERVICE_Client *client, - void *internal_cls) -{ - struct MyClient *cl = cls; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls; - - for (cl = tc_h->client_head; NULL != cl; cl = cl->next) - { - if (cl->client != client) - continue; - GNUNET_CONTAINER_DLL_remove (tc_h->client_head, - tc_h->client_tail, - cl); - if (cl->c_mq == tc_h->c_mq) - tc_h->c_mq = NULL; - GNUNET_free (cl); - break; - } - LOG (GNUNET_ERROR_TYPE_DEBUG, "Client disconnected.\n"); -} - - -/** - * Message was transmitted. Process the request. - * - * @param cls the client - * @param sma the send message that was sent - */ -static void -handle_send_message_ack (void *cls, - const struct GNUNET_TRANSPORT_SendMessageToAck *sma) -{ - struct MyClient *client = cls; - GNUNET_SERVICE_client_continue (client->client); - // NOP -} - - -/** - * @brief Start the communicator part of the transport service - * - * @param communicator_available Callback to be called when a new communicator - * becomes available - * @param cfg Configuration - */ -static void -transport_communicator_start ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) -{ - struct GNUNET_MQ_MessageHandler mh[] = { - GNUNET_MQ_hd_var_size (communicator_available, - GNUNET_MESSAGE_TYPE_TRANSPORT_NEW_COMMUNICATOR, - struct GNUNET_TRANSPORT_CommunicatorAvailableMessage, - tc_h), - GNUNET_MQ_hd_var_size (communicator_backchannel, - GNUNET_MESSAGE_TYPE_TRANSPORT_COMMUNICATOR_BACKCHANNEL, - struct GNUNET_TRANSPORT_CommunicatorBackchannel, - tc_h), - GNUNET_MQ_hd_var_size (add_address, - GNUNET_MESSAGE_TYPE_TRANSPORT_ADD_ADDRESS, - struct GNUNET_TRANSPORT_AddAddressMessage, - tc_h), - // GNUNET_MQ_hd_fixed_size (del_address, - // GNUNET_MESSAGE_TYPE_TRANSPORT_DEL_ADDRESS, - // struct GNUNET_TRANSPORT_DelAddressMessage, - // NULL), - GNUNET_MQ_hd_var_size (incoming_msg, - GNUNET_MESSAGE_TYPE_TRANSPORT_INCOMING_MSG, - struct GNUNET_TRANSPORT_IncomingMessage, - tc_h), - GNUNET_MQ_hd_fixed_size (queue_create_ok, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_OK, - struct GNUNET_TRANSPORT_CreateQueueResponse, - tc_h), - GNUNET_MQ_hd_fixed_size (queue_create_fail, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE_FAIL, - struct GNUNET_TRANSPORT_CreateQueueResponse, - tc_h), - GNUNET_MQ_hd_var_size (add_queue_message, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_SETUP, - struct GNUNET_TRANSPORT_AddQueueMessage, - tc_h), - GNUNET_MQ_hd_fixed_size (update_queue_message, - GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_UPDATE, - struct GNUNET_TRANSPORT_UpdateQueueMessage, - tc_h), - // GNUNET_MQ_hd_fixed_size (del_queue_message, - // GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_TEARDOWN, - // struct GNUNET_TRANSPORT_DelQueueMessage, - // NULL), - GNUNET_MQ_hd_fixed_size (send_message_ack, - GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG_ACK, - struct GNUNET_TRANSPORT_SendMessageToAck, - tc_h), - GNUNET_MQ_handler_end () - }; - - - tc_h->sh = GNUNET_SERVICE_start ("transport", - tc_h->cfg, - &connect_cb, - &disconnect_cb, - tc_h, - mh); - GNUNET_assert (NULL != tc_h->sh); -} - - -/** - * @brief Task run at shutdown to kill communicator and clean up - * - * @param cls Closure - Process of communicator - */ -static void -shutdown_process (struct GNUNET_OS_Process *proc) -{ - if (0 != GNUNET_OS_process_kill (proc, SIGTERM)) - { - LOG (GNUNET_ERROR_TYPE_WARNING, - "Error shutting down process with SIGERM, trying SIGKILL\n"); - if (0 != GNUNET_OS_process_kill (proc, SIGKILL)) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - "Error shutting down process with SIGERM and SIGKILL\n"); - } - } - GNUNET_OS_process_destroy (proc); -} - -static void -shutdown_peerstore (void *cls) -{ - struct GNUNET_OS_Process *proc = cls; - shutdown_process (proc); -} - -static void -shutdown_communicator (void *cls) -{ - struct GNUNET_OS_Process *proc = cls; - shutdown_process (proc); -} - - -/** - * @brief Start the communicator - * - * @param cfgname Name of the communicator - */ -static void -communicator_start ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, - const char *binary_name) -{ - char *binary; - char *loprefix; - char *section_name; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "communicator_start\n"); - - section_name = strchr (binary_name, '-'); - section_name++; - - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (tc_h->cfg, - section_name, - "PREFIX", - &loprefix)) - loprefix = GNUNET_strdup (""); - - - binary = GNUNET_OS_get_libexec_binary_path (binary_name); - tc_h->c_proc = GNUNET_OS_start_process_s (GNUNET_OS_INHERIT_STD_OUT_AND_ERR, - NULL, - loprefix, - binary, - binary_name, - "-c", - tc_h->cfg_filename, - NULL); - if (NULL == tc_h->c_proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start communicator!"); - return; - } - LOG (GNUNET_ERROR_TYPE_INFO, "started communicator\n"); - GNUNET_free (binary); -} - - -/** - * @brief Task run at shutdown to kill communicator and clean up - * - * @param cls Closure - Process of communicator - */ -static void -shutdown_nat (void *cls) -{ - struct GNUNET_OS_Process *proc = cls; - shutdown_process (proc); -} - - -/** - * @brief Task run at shutdown to kill the resolver process - * - * @param cls Closure - Process of communicator - */ -static void -shutdown_resolver (void *cls) -{ - struct GNUNET_OS_Process *proc = cls; - shutdown_process (proc); -} - - -static void -resolver_start (struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) -{ - char *binary; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "resolver_start\n"); - binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-resolver"); - tc_h->resolver_proc = GNUNET_OS_start_process ( - GNUNET_OS_INHERIT_STD_OUT_AND_ERR - | GNUNET_OS_USE_PIPE_CONTROL, - NULL, - NULL, - NULL, - binary, - "gnunet-service-resolver", - "-c", - tc_h->cfg_filename, - NULL); - if (NULL == tc_h->resolver_proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start resolver service!"); - return; - } - LOG (GNUNET_ERROR_TYPE_INFO, "started resolver service\n"); - GNUNET_free (binary); - -} - - -/** - * @brief Start Peerstore - * - */ -static void -peerstore_start ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) -{ - char *binary; - - binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-peerstore"); - tc_h->ps_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR, - NULL, - NULL, - NULL, - binary, - "gnunet-service-peerstore", - "-c", - tc_h->cfg_filename, - NULL); - if (NULL == tc_h->ps_proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start Peerstore!"); - return; - } - LOG (GNUNET_ERROR_TYPE_INFO, "started Peerstore\n"); - GNUNET_free (binary); -} - -/** - * @brief Start NAT - * - */ -static void -nat_start ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) -{ - char *binary; - - LOG (GNUNET_ERROR_TYPE_DEBUG, "nat_start\n"); - binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-nat"); - tc_h->nat_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_OUT_AND_ERR - | GNUNET_OS_USE_PIPE_CONTROL, - NULL, - NULL, - NULL, - binary, - "gnunet-service-nat", - "-c", - tc_h->cfg_filename, - NULL); - if (NULL == tc_h->nat_proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to start NAT!"); - return; - } - LOG (GNUNET_ERROR_TYPE_INFO, "started NAT\n"); - GNUNET_free (binary); -} - - -/** - * @brief Start communicator part of transport service and communicator - * - * @param service_name Name of the service - * @param cfg Configuration handle - * @param communicator_available_cb Callback that is called when a new - * @param add_address_cb Callback that is called when a new - * communicator becomes available - * @param cb_cls Closure to @a communicator_available_cb and @a - * - * @return Handle to the communicator duo - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * -GNUNET_TRANSPORT_TESTING_transport_communicator_service_start ( - const char *service_name, - const char *binary_name, - const char *cfg_filename, - const struct GNUNET_PeerIdentity *peer_id, - GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback - communicator_available_cb, - GNUNET_TRANSPORT_TESTING_AddAddressCallback add_address_cb, - GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback queue_create_reply_cb, - GNUNET_TRANSPORT_TESTING_AddQueueCallback add_queue_cb, - GNUNET_TRANSPORT_TESTING_IncomingMessageCallback incoming_message_cb, - GNUNET_TRANSPORT_TESTING_BackchannelCallback bc_cb, - void *cb_cls) -{ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h; - - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Starting new transport/communicator combo with config %s\n", - cfg_filename); - tc_h = - GNUNET_new (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle); - tc_h->cfg_filename = GNUNET_strdup (cfg_filename); - tc_h->cfg = GNUNET_CONFIGURATION_create (); - if ((GNUNET_SYSERR == GNUNET_CONFIGURATION_load (tc_h->cfg, cfg_filename))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _ ("Malformed configuration file `%s', exit ...\n"), - cfg_filename); - GNUNET_free (tc_h->cfg_filename); - GNUNET_CONFIGURATION_destroy (tc_h->cfg); - GNUNET_free (tc_h); - return NULL; - } - tc_h->bc_enabled = GNUNET_CONFIGURATION_get_value_yesno (tc_h->cfg, - "communicator-test", - "BACKCHANNEL_ENABLED"); - tc_h->communicator_available_cb = communicator_available_cb; - tc_h->add_address_cb = add_address_cb; - tc_h->queue_create_reply_cb = queue_create_reply_cb; - tc_h->add_queue_cb = add_queue_cb; - tc_h->incoming_msg_cb = incoming_message_cb; - tc_h->bc_cb = bc_cb; - tc_h->peer_id = *peer_id; - tc_h->cb_cls = cb_cls; - - /* Start communicator part of service */ - transport_communicator_start (tc_h); - /* Start NAT */ - nat_start (tc_h); - /* Start resolver service */ - resolver_start (tc_h); - /* Start peerstore service */ - peerstore_start (tc_h); - /* Schedule start communicator */ - communicator_start (tc_h, - binary_name); - return tc_h; -} - - -void -GNUNET_TRANSPORT_TESTING_transport_communicator_service_stop ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h) -{ - shutdown_communicator (tc_h->c_proc); - shutdown_service (tc_h->sh); - shutdown_nat (tc_h->nat_proc); - shutdown_resolver (tc_h->resolver_proc); - shutdown_peerstore (tc_h->ps_proc); - GNUNET_CONFIGURATION_destroy (tc_h->cfg); - GNUNET_free (tc_h); -} - - -/** - * @brief Instruct communicator to open a queue - * - * @param tc_h Handle to communicator which shall open queue - * @param peer_id Towards which peer - * @param address For which address - */ -void -GNUNET_TRANSPORT_TESTING_transport_communicator_open_queue ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, - const struct GNUNET_PeerIdentity *peer_id, - const char *address) -{ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; - static uint32_t idgen; - char *prefix; - struct GNUNET_TRANSPORT_CreateQueue *msg; - struct GNUNET_MQ_Envelope *env; - size_t alen; - - tc_queue = - GNUNET_new (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue); - tc_queue->tc_h = tc_h; - prefix = GNUNET_HELLO_address_to_prefix (address); - if (NULL == prefix) - { - GNUNET_break (0); /* We got an invalid address!? */ - GNUNET_free (tc_queue); - return; - } - GNUNET_free (prefix); - alen = strlen (address) + 1; - env = - GNUNET_MQ_msg_extra (msg, alen, GNUNET_MESSAGE_TYPE_TRANSPORT_QUEUE_CREATE); - msg->request_id = htonl (idgen++); - tc_queue->qid = msg->request_id; - msg->receiver = *peer_id; - tc_queue->peer_id = *peer_id; - memcpy (&msg[1], address, alen); - if (NULL != tc_h->c_mq) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Sending queue create immediately\n"); - GNUNET_MQ_send (tc_h->c_mq, env); - } - else - { - tc_queue->open_queue_env = env; - } - GNUNET_CONTAINER_DLL_insert (tc_h->queue_head, tc_h->queue_tail, tc_queue); -} - - -/** - * @brief Instruct communicator to send data - * - * @param tc_queue The queue to use for sending - * @param cont function to call when done sending - * @param cont_cls closure for @a cont - * @param payload Data to send - * @param payload_size Size of the @a payload - */ -void -GNUNET_TRANSPORT_TESTING_transport_communicator_send - (struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h, - GNUNET_SCHEDULER_TaskCallback cont, - void *cont_cls, - const void *payload, - size_t payload_size) -{ - struct GNUNET_MessageHeader *mh; - struct GNUNET_TRANSPORT_SendMessageTo *msg; - struct GNUNET_MQ_Envelope *env; - size_t inbox_size; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue; - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *tc_queue_tmp; - - tc_queue = NULL; - for (tc_queue_tmp = tc_h->queue_head; - NULL != tc_queue_tmp; - tc_queue_tmp = tc_queue_tmp->next) - { - if (tc_queue_tmp->q_len <= 0) - continue; - if (NULL == tc_queue) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Selecting queue with prio %u, len %" PRIu64 " and MTU %u\n", - tc_queue_tmp->priority, - tc_queue_tmp->q_len, - tc_queue_tmp->mtu); - tc_queue = tc_queue_tmp; - continue; - } - if (tc_queue->priority < tc_queue_tmp->priority) - { - LOG (GNUNET_ERROR_TYPE_DEBUG, - "Selecting queue with prio %u, len %" PRIu64 " and MTU %u\n", - tc_queue_tmp->priority, - tc_queue_tmp->q_len, - tc_queue_tmp->mtu); - tc_queue = tc_queue_tmp; - } - } - GNUNET_assert (NULL != tc_queue); - // Uncomment this for alternativ 1 of backchannel functionality - if (tc_queue->q_len != GNUNET_TRANSPORT_QUEUE_LENGTH_UNLIMITED) - tc_queue->q_len--; - // Until here for alternativ 1 - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Sending message\n"); - inbox_size = sizeof (struct GNUNET_MessageHeader) + payload_size; - env = GNUNET_MQ_msg_extra (msg, - inbox_size, - GNUNET_MESSAGE_TYPE_TRANSPORT_SEND_MSG); - GNUNET_assert (NULL != env); - msg->qid = htonl (tc_queue->qid); - msg->mid = tc_queue->mid++; - msg->receiver = tc_queue->peer_id; - mh = (struct GNUNET_MessageHeader *) &msg[1]; - mh->size = htons (inbox_size); - mh->type = GNUNET_MESSAGE_TYPE_DUMMY; - memcpy (&mh[1], - payload, - payload_size); - if (NULL != cont) - GNUNET_MQ_notify_sent (env, - cont, - cont_cls); - GNUNET_MQ_send (tc_queue->tc_h->c_mq, - env); -} diff --git a/src/transport/transport-testing2.h b/src/transport/transport-testing2.h deleted file mode 100644 index 04f75fc88..000000000 --- a/src/transport/transport-testing2.h +++ /dev/null @@ -1,355 +0,0 @@ -/* - This file is part of GNUnet. - Copyright (C) 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 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 . - - SPDX-License-Identifier: AGPL3.0-or-later - */ - -/** - * @file transport/transport-testing2.h - * @brief functions and structures related to testing-tng - * @author Christian Grothoff - * @author Julius Bünger - */ -#include "platform.h" -#include "gnunet_util_lib.h" -#include "gnunet_ats_transport_service.h" -#include "transport.h" - -/** - * @brief Queue of a communicator and some context - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue; - - -/** - * @brief Handle/Context to a single transmission - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorTransmission; - -/** - * @brief Function signature for callbacks that are called when new - * backchannel message arrived - * - * @param cls Closure - * @param msg Backchannel message - * @param pid Target peer - */ -typedef struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * -(*GNUNET_TRANSPORT_TESTING_BackchannelCallback)(void *cls, - struct GNUNET_MessageHeader *msg, - struct GNUNET_PeerIdentity *pid); - - -/** - * @brief Function signature for callbacks that are called when new - * communicators become available - * - * @param cls Closure - * @param tc_h Communicator handle - * @param cc Characteristics of communicator - * @param address_prefix Prefix of the address - */ -typedef void -(*GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback)(void *cls, - struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - enum - GNUNET_TRANSPORT_CommunicatorCharacteristics - cc, - char *address_prefix); - - -/** - * @brief Receive information about the address of a communicator. - * - * @param cls Closure - * @param tc_h Communicator handle - * @param address Address represented as string - * @param expiration Expiration - * @param aid Aid - * @param nt Network Type - */ -typedef void -(*GNUNET_TRANSPORT_TESTING_AddAddressCallback)(void *cls, - struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - const char *address, - struct GNUNET_TIME_Relative - expiration, - uint32_t aid, - enum GNUNET_NetworkType nt); - - -/** - * @brief Get informed about the success of a queue request. - * - * @param cls Closure - * @param tc_h Communicator handle - * @param will_try #GNUNET_YES if communicator will try to create queue - */ -typedef void -(*GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback)(void *cls, - struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - int will_try); - - -/** - * @brief Handle opening of queue - * - * @param cls Closure - * @param tc_h Communicator handle - * @param tc_queue Handle to newly opened queue - */ -typedef void -(*GNUNET_TRANSPORT_TESTING_AddQueueCallback)(void *cls, - struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue - *tc_queue, - size_t mtu); - - -/** - * @brief Handle an incoming message - * - * @param cls Closure - * @param tc_h Handle to the receiving communicator - * @param msg Received message - */ -typedef void -(*GNUNET_TRANSPORT_TESTING_IncomingMessageCallback)(void *cls, - struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - const char*payload, - size_t payload_len); - -/** - * @brief Handle to a transport communicator - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle -{ - /** - * Clients - */ - struct MyClient *client_head; - struct MyClient *client_tail; - - /** - * @brief Handle to the client - */ - struct GNUNET_MQ_Handle *c_mq; - - /** - * @brief Handle to the configuration - */ - struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * @brief File name of configuration file - */ - char *cfg_filename; - - struct GNUNET_PeerIdentity peer_id; - - /** - * @brief Handle to the transport service - */ - struct GNUNET_SERVICE_Handle *tsh; - - /** - * @brief Task that will be run on shutdown to stop and clean transport - * service - */ - struct GNUNET_SCHEDULER_Task *ts_shutdown_task; - - - /** - * @brief Process of the communicator - */ - struct GNUNET_OS_Process *c_proc; - - /** - * NAT process - */ - struct GNUNET_OS_Process *nat_proc; - - /** - * resolver service process - */ - struct GNUNET_OS_Process *resolver_proc; - - /** - * peerstore service process - */ - struct GNUNET_OS_Process *ps_proc; - - /** - * @brief Task that will be run on shutdown to stop and clean communicator - */ - struct GNUNET_SCHEDULER_Task *c_shutdown_task; - - /** - * @brief Characteristics of the communicator - */ - enum GNUNET_TRANSPORT_CommunicatorCharacteristics c_characteristics; - - /** - * @brief Specifies supported addresses - */ - char *c_addr_prefix; - - /** - * @brief Specifies supported addresses - */ - char *c_address; - - /** - * @brief Head of the DLL of queues associated with this communicator - */ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *queue_head; - - /** - * @brief Tail of the DLL of queues associated with this communicator - */ - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *queue_tail; - - /* Callbacks + Closures */ - /** - * @brief Callback called when a new communicator connects - */ - GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback - communicator_available_cb; - - /** - * @brief Callback called when a new communicator connects - */ - GNUNET_TRANSPORT_TESTING_AddAddressCallback add_address_cb; - - /** - * @brief Callback called when a new communicator connects - */ - GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback queue_create_reply_cb; - - /** - * @brief Callback called when a new communicator connects - */ - GNUNET_TRANSPORT_TESTING_AddQueueCallback add_queue_cb; - - /** - * @brief Callback called when a new communicator connects - */ - GNUNET_TRANSPORT_TESTING_IncomingMessageCallback incoming_msg_cb; - - /** - * @brief Backchannel callback - */ - GNUNET_TRANSPORT_TESTING_BackchannelCallback bc_cb; - - /** - * Our service handle - */ - struct GNUNET_SERVICE_Handle *sh; - - /** - * @brief Closure to the callback - */ - void *cb_cls; - - /** - * Backchannel supported - */ - int bc_enabled; -}; - -/** - * @brief Start communicator part of transport service and communicator - * - * @param service_name Name of the service - * @param cfg Configuration handle - * @param communicator_available Callback that is called when a new - * communicator becomes available - * @param add_address_cb Callback handling new addresses - * @param queue_create_reply_cb Callback handling success of queue requests - * @param add_queue_cb Callback handling freshly created queues - * @param incoming_message_cb Callback handling incoming messages - * @param cb_cls Closure to @p communicator_available - * - * @return Handle to the communicator duo - */ -struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle * -GNUNET_TRANSPORT_TESTING_transport_communicator_service_start ( - const char *service_name, - const char *binary_name, - const char *cfg_filename, - const struct GNUNET_PeerIdentity *peer_id, - GNUNET_TRANSPORT_TESTING_CommunicatorAvailableCallback - communicator_available_cb, - GNUNET_TRANSPORT_TESTING_AddAddressCallback add_address_cb, - GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback queue_create_reply_cb, - GNUNET_TRANSPORT_TESTING_AddQueueCallback add_queue_cb, - GNUNET_TRANSPORT_TESTING_IncomingMessageCallback incoming_message_cb, - GNUNET_TRANSPORT_TESTING_BackchannelCallback bc_cb, - void *cb_cls); - - -void -GNUNET_TRANSPORT_TESTING_transport_communicator_service_stop ( - struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h); - - -/** - * @brief Instruct communicator to open a queue - * - * @param tc_h Handle to communicator which shall open queue - * @param peer_id Towards which peer - * @param address For which address - */ -void -GNUNET_TRANSPORT_TESTING_transport_communicator_open_queue (struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - const struct - GNUNET_PeerIdentity - *peer_id, - const char *address); - - -/** - * @brief Instruct communicator to send data - * - * @param tc_queue The queue to use for sending - * @param cont function to call when done sending - * @param cont_cls closure for @a cont - * @param payload Data to send - * @param payload_size Size of the @a payload - */ -void -GNUNET_TRANSPORT_TESTING_transport_communicator_send (struct - GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle - *tc_h, - GNUNET_SCHEDULER_TaskCallback - cont, - void *cont_cls, - const void *payload, - size_t payload_size); -- cgit v1.2.3 From 172ef59b934c067dd7ec8e72650e196c7ae2a04b Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Mon, 30 Nov 2020 18:08:25 +0900 Subject: -start TNG transport test logic --- src/transport/Makefile.am | 5 + src/transport/transport-testing2.c | 923 ++++++++++++++++++++++++++++++++++++ src/transport/transport-testing2.h | 924 +++++++++++++++++++++++++++++++++++++ 3 files changed, 1852 insertions(+) create mode 100644 src/transport/transport-testing2.c create mode 100644 src/transport/transport-testing2.h diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am index 477973af9..605d89cc0 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am @@ -168,10 +168,15 @@ libgnunettransporttesting_la_LDFLAGS = \ $(GN_LIB_LDFLAGS) libgnunettransporttesting2_la_SOURCES = \ + transport-testing2.c transport-testing2.h \ transport-testing-communicator.c transport-testing-communicator.h libgnunettransporttesting2_la_LIBADD = \ libgnunettransport.la \ + libgnunettransportapplication.la \ + libgnunettransportcore.la \ + $(top_builddir)/src/testing/libgnunettesting.la \ $(top_builddir)/src/hello/libgnunethello.la \ + $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ $(top_builddir)/src/util/libgnunetutil.la libgnunettransporttesting2_la_LDFLAGS = \ $(GN_LIBINTL) \ diff --git a/src/transport/transport-testing2.c b/src/transport/transport-testing2.c new file mode 100644 index 000000000..ca338c52f --- /dev/null +++ b/src/transport/transport-testing2.c @@ -0,0 +1,923 @@ +/* + This file is part of GNUnet. + Copyright (C) 2006, 2009, 2015, 2016 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 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ +/** + * @file transport-testing.c + * @brief testing lib for transport service + * @author Matthias Wachs + * @author Christian Grothoff + */ +#include "transport-testing2.h" + + +#define LOG(kind, ...) GNUNET_log_from (kind, "transport-testing", __VA_ARGS__) + + +static struct GNUNET_TRANSPORT_TESTING_PeerContext * +find_peer_context (struct GNUNET_TRANSPORT_TESTING_Handle *tth, + const struct GNUNET_PeerIdentity *peer) +{ + struct GNUNET_TRANSPORT_TESTING_PeerContext *t; + + for (t = tth->p_head; NULL != t; t = t->next) + if (0 == memcmp (&t->id, + peer, + sizeof(struct GNUNET_PeerIdentity))) + return t; + return NULL; +} + + +/** + * Find any connecting context matching the given pair of peers. + * + * @param p1 first peer + * @param p2 second peer + * @param cb function to call + * @param cb_cls closure for @a cb + */ +void +GNUNET_TRANSPORT_TESTING_find_connecting_context (struct + GNUNET_TRANSPORT_TESTING_PeerContext + *p1, + struct + GNUNET_TRANSPORT_TESTING_PeerContext + *p2, + GNUNET_TRANSPORT_TESTING_ConnectContextCallback + cb, + void *cb_cls) +{ + struct GNUNET_TRANSPORT_TESTING_Handle *tth = p1->tth; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; + + for (cc = tth->cc_head; NULL != cc; cc = ccn) + { + ccn = cc->next; + if ((cc->p1 == p1) && + (cc->p2 == p2)) + cb (cb_cls, + cc); + } +} + + +static void +set_p1c (void *cls, + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) +{ + int *found = cls; + + if (NULL != found) + *found = GNUNET_YES; + cx->p1_c = GNUNET_YES; +} + + +static void +set_mq (void *cls, + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) +{ + struct GNUNET_MQ_Handle *mq = cls; + + cx->mq = mq; +} + + +static void +set_p2c (void *cls, + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) +{ + int *found = cls; + + if (NULL != found) + *found = GNUNET_YES; + cx->p2_c = GNUNET_YES; +} + + +static void +clear_p1c (void *cls, + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) +{ + int *found = cls; + + if (NULL != found) + *found = GNUNET_YES; + cx->p1_c = GNUNET_NO; +} + + +static void +clear_p2c (void *cls, + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx) +{ + int *found = cls; + + if (NULL != found) + *found = GNUNET_YES; + cx->p2_c = GNUNET_NO; +} + + +static void * +notify_connect (void *cls, + const struct GNUNET_PeerIdentity *peer, + struct GNUNET_MQ_Handle *mq) +{ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cls; + struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth; + char *p2_s; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p2; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; + int found; + void *ret; + + p2 = find_peer_context (p->tth, + peer); + if (NULL != p->nc) + ret = p->nc (p->cb_cls, + peer, + mq); + else + ret = NULL; + + if (NULL != p2) + GNUNET_asprintf (&p2_s, + "%u (`%s')", + p2->no, + GNUNET_i2s (&p2->id)); + else + GNUNET_asprintf (&p2_s, + "`%s'", + GNUNET_i2s (peer)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peers %s connected to peer %u (`%s')\n", + p2_s, + p->no, + GNUNET_i2s (&p->id)); + GNUNET_free (p2_s); + /* update flags in connecting contexts */ + found = GNUNET_NO; + GNUNET_TRANSPORT_TESTING_find_connecting_context (p, + p2, + &set_p1c, + &found); + if (GNUNET_NO == found) + { + cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest); + cc->p1 = p; + cc->p2 = p2; + cc->p1_c = GNUNET_YES; + GNUNET_CONTAINER_DLL_insert (tth->cc_head, + tth->cc_tail, + cc); + } + found = GNUNET_NO; + GNUNET_TRANSPORT_TESTING_find_connecting_context (p2, + p, + &set_p2c, + &found); + if (GNUNET_NO == found) + { + cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest); + cc->p1 = p2; + cc->p2 = p; + cc->p1_c = GNUNET_YES; + GNUNET_CONTAINER_DLL_insert (tth->cc_head, + tth->cc_tail, + cc); + } + GNUNET_TRANSPORT_TESTING_find_connecting_context (p, + p2, + &set_mq, + mq); + /* update set connected flag for all requests */ + for (cc = tth->cc_head; NULL != cc; cc = cc->next) + { + if (GNUNET_YES == cc->connected) + continue; + if ((GNUNET_YES == cc->p1_c) && + (GNUNET_YES == cc->p2_c)) + { + cc->connected = GNUNET_YES; + /* stop trying to connect */ + if (NULL != cc->tct) + { + GNUNET_SCHEDULER_cancel (cc->tct); + cc->tct = NULL; + } + if (NULL != cc->ats_sh) + { + GNUNET_ATS_connectivity_suggest_cancel (cc->ats_sh); + cc->ats_sh = NULL; + } + } + } + /* then notify application */ + for (cc = tth->cc_head; NULL != cc; cc = ccn) + { + ccn = cc->next; + if ((GNUNET_YES == cc->connected) && + (NULL != cc->cb)) + { + cc->cb (cc->cb_cls); + cc->cb = NULL; /* only notify once! */ + } + } + return ret; +} + + +/** + * Offer the current HELLO of P2 to P1. + * + * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectRequest` + */ +static void +offer_hello (void *cls); + + +static void +notify_disconnect (void *cls, + const struct GNUNET_PeerIdentity *peer, + void *handler_cls) +{ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cls; + struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth; + char *p2_s; + /* Find PeerContext */ + int no = 0; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p2 = NULL; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + + p2 = find_peer_context (p->tth, + peer); + no = p->no; + if (NULL != p2) + GNUNET_asprintf (&p2_s, + "%u (`%s')", + p2->no, + GNUNET_i2s (&p2->id)); + else + GNUNET_asprintf (&p2_s, + "`%s'", + GNUNET_i2s (peer)); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peers %s disconnected from peer %u (`%s')\n", + p2_s, + no, + GNUNET_i2s (&p->id)); + GNUNET_free (p2_s); + /* notify about disconnect */ + if (NULL != p->nd) + p->nd (p->cb_cls, + peer, + handler_cls); + if (NULL == p2) + return; + /* clear MQ, it is now invalid */ + GNUNET_TRANSPORT_TESTING_find_connecting_context (p, + p2, + &set_mq, + NULL); + /* update set connected flags for all requests */ + GNUNET_TRANSPORT_TESTING_find_connecting_context (p, + p2, + &clear_p1c, + NULL); + GNUNET_TRANSPORT_TESTING_find_connecting_context (p2, + p, + &clear_p2c, + NULL); + /* resume connectivity requests as necessary */ + for (cc = tth->cc_head; NULL != cc; cc = cc->next) + { + if (GNUNET_NO == cc->connected) + continue; + if ((GNUNET_YES != cc->p1_c) || + (GNUNET_YES != cc->p2_c)) + { + cc->connected = GNUNET_NO; + /* start trying to connect */ + if (NULL == cc->tct) + cc->tct = GNUNET_SCHEDULER_add_now (&offer_hello, + cc); + if (NULL == cc->ats_sh) + cc->ats_sh = GNUNET_ATS_connectivity_suggest (cc->p1->ats, + &p2->id, + 1); + } + } +} + + +static void +hello_iter_cb (void *cb_cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cb_cls; + //Check record type et al? + p->hello = GNUNET_malloc (record->value_size); + memcpy (p->hello, record->value, record->value_size); + + GNUNET_PEERSTORE_iterate_cancel (p->pic); + if (NULL != p->start_cb) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peer %u (`%s') successfully started\n", + p->no, + GNUNET_i2s (&p->id)); + p->start_cb (p->start_cb_cls); + p->start_cb = NULL; + } +} + + +/** + * Start a peer with the given configuration + * @param tth the testing handle + * @param cfgname configuration file + * @param peer_id a unique number to identify the peer + * @param handlers functions for receiving messages + * @param nc connect callback + * @param nd disconnect callback + * @param cb_cls closure for callback + * @param start_cb start callback + * @param start_cb_cls closure for callback + * @return the peer context + */ +struct GNUNET_TRANSPORT_TESTING_PeerContext * +GNUNET_TRANSPORT_TESTING_start_peer (struct + GNUNET_TRANSPORT_TESTING_Handle *tth, + const char *cfgname, + int peer_id, + const struct + GNUNET_MQ_MessageHandler *handlers, + GNUNET_TRANSPORT_NotifyConnect nc, + GNUNET_TRANSPORT_NotifyDisconnect nd, + void *cb_cls, + GNUNET_SCHEDULER_TaskCallback start_cb, + void *start_cb_cls) +{ + char *emsg = NULL; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p; + struct GNUNET_PeerIdentity dummy; + unsigned int i; + + if (GNUNET_NO == GNUNET_DISK_file_test (cfgname)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "File not found: `%s'\n", + cfgname); + return NULL; + } + + p = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_PeerContext); + p->tth = tth; + p->nc = nc; + p->nd = nd; + if (NULL != handlers) + { + for (i = 0; NULL != handlers[i].cb; i++) + ; + p->handlers = GNUNET_new_array (i + 1, + struct GNUNET_MQ_MessageHandler); + GNUNET_memcpy (p->handlers, + handlers, + i * sizeof(struct GNUNET_MQ_MessageHandler)); + } + if (NULL != cb_cls) + p->cb_cls = cb_cls; + else + p->cb_cls = p; + p->start_cb = start_cb; + if (NULL != start_cb_cls) + p->start_cb_cls = start_cb_cls; + else + p->start_cb_cls = p; + GNUNET_CONTAINER_DLL_insert (tth->p_head, + tth->p_tail, + p); + + /* Create configuration and call testing lib to modify it */ + p->cfg = GNUNET_CONFIGURATION_create (); + GNUNET_assert (GNUNET_OK == + GNUNET_CONFIGURATION_load (p->cfg, cfgname)); + if (GNUNET_SYSERR == + GNUNET_TESTING_configuration_create (tth->tl_system, + p->cfg)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Testing library failed to create unique configuration based on `%s'\n", + cfgname); + GNUNET_CONFIGURATION_destroy (p->cfg); + GNUNET_free (p); + return NULL; + } + + p->no = peer_id; + /* Configure peer with configuration */ + p->peer = GNUNET_TESTING_peer_configure (tth->tl_system, + p->cfg, + p->no, + NULL, + &emsg); + if (NULL == p->peer) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Testing library failed to create unique configuration based on `%s': `%s'\n", + cfgname, + emsg); + GNUNET_TRANSPORT_TESTING_stop_peer (p); + GNUNET_free (emsg); + return NULL; + } + GNUNET_free (emsg); + if (GNUNET_OK != GNUNET_TESTING_peer_start (p->peer)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Testing library failed to create unique configuration based on `%s'\n", + cfgname); + GNUNET_TRANSPORT_TESTING_stop_peer (p); + return NULL; + } + + memset (&dummy, + '\0', + sizeof(dummy)); + GNUNET_TESTING_peer_get_identity (p->peer, + &p->id); + if (0 == memcmp (&dummy, + &p->id, + sizeof(struct GNUNET_PeerIdentity))) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Testing library failed to obtain peer identity for peer %u\n", + p->no); + GNUNET_TRANSPORT_TESTING_stop_peer (p); + return NULL; + } + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peer %u configured with identity `%s'\n", + p->no, + GNUNET_i2s_full (&p->id)); + p->tmh = GNUNET_TRANSPORT_manipulation_connect (p->cfg); + p->th = GNUNET_TRANSPORT_core_connect (p->cfg, + NULL, + handlers, + p, + ¬ify_connect, + ¬ify_disconnect); + if ((NULL == p->th) || + (NULL == p->tmh)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Failed to connect to transport service for peer `%s': `%s'\n", + cfgname, + emsg); + GNUNET_TRANSPORT_TESTING_stop_peer (p); + return NULL; + } + p->ats = GNUNET_ATS_connectivity_init (p->cfg); + if (NULL == p->ats) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Failed to connect to ATS service for peer `%s': `%s'\n", + cfgname, + emsg); + GNUNET_TRANSPORT_TESTING_stop_peer (p); + return NULL; + } + p->pic = GNUNET_PEERSTORE_iterate (p->ph, + "transport", + &p->id, + GNUNET_PEERSTORE_TRANSPORT_HELLO_KEY, + hello_iter_cb, + p); + GNUNET_assert (NULL != p->pic); + + return p; +} + + +/** + * Stops and restarts the given peer, sleeping (!) for 5s in between. + * + * @param p the peer + * @param restart_cb callback to call when restarted + * @param restart_cb_cls callback closure + * @return #GNUNET_OK in success otherwise #GNUNET_SYSERR + */ +int +GNUNET_TRANSPORT_TESTING_restart_peer (struct + GNUNET_TRANSPORT_TESTING_PeerContext *p, + GNUNET_SCHEDULER_TaskCallback restart_cb, + void *restart_cb_cls) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; + + /* shutdown */ + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Stopping peer %u (`%s')\n", + p->no, + GNUNET_i2s (&p->id)); + if (NULL != p->pic) + { + GNUNET_PEERSTORE_iterate_cancel (p->pic); + p->pic = NULL; + } + if (NULL != p->th) + { + GNUNET_TRANSPORT_core_disconnect (p->th); + p->th = NULL; + } + if (NULL != p->tmh) + { + GNUNET_TRANSPORT_manipulation_disconnect (p->tmh); + p->tmh = NULL; + } + for (cc = p->tth->cc_head; NULL != cc; cc = ccn) + { + ccn = cc->next; + if ((cc->p1 == p) || + (cc->p2 == p)) + GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc); + } + if (NULL != p->ats) + { + GNUNET_ATS_connectivity_done (p->ats); + p->ats = NULL; + } + if (GNUNET_SYSERR == + GNUNET_TESTING_peer_stop (p->peer)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Failed to stop peer %u (`%s')\n", + p->no, + GNUNET_i2s (&p->id)); + return GNUNET_SYSERR; + } + + sleep (5); // YUCK! + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Restarting peer %u (`%s')\n", + p->no, + GNUNET_i2s (&p->id)); + /* restart */ + if (GNUNET_SYSERR == GNUNET_TESTING_peer_start (p->peer)) + { + LOG (GNUNET_ERROR_TYPE_ERROR, + "Failed to restart peer %u (`%s')\n", + p->no, + GNUNET_i2s (&p->id)); + return GNUNET_SYSERR; + } + + GNUNET_assert (NULL == p->start_cb); + p->start_cb = restart_cb; + p->start_cb_cls = restart_cb_cls; + + p->th = GNUNET_TRANSPORT_core_connect (p->cfg, + NULL, + p->handlers, + p, + ¬ify_connect, + ¬ify_disconnect); + GNUNET_assert (NULL != p->th); + p->ats = GNUNET_ATS_connectivity_init (p->cfg); + p->pic = GNUNET_PEERSTORE_iterate (p->ph, + "transport", + &p->id, + GNUNET_PEERSTORE_TRANSPORT_HELLO_KEY, + hello_iter_cb, + p); + GNUNET_assert (NULL != p->pic); + return GNUNET_OK; +} + + +/** + * Shutdown the given peer + * + * @param p the peer + */ +void +GNUNET_TRANSPORT_TESTING_stop_peer (struct + GNUNET_TRANSPORT_TESTING_PeerContext *p) +{ + struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; + + for (cc = tth->cc_head; NULL != cc; cc = ccn) + { + ccn = cc->next; + if ((cc->p1 == p) || + (cc->p2 == p)) + GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc); + } + if (NULL != p->pic) + { + GNUNET_PEERSTORE_iterate_cancel (p->pic); + p->pic = NULL; + } + if (NULL != p->tmh) + { + GNUNET_TRANSPORT_manipulation_disconnect (p->tmh); + p->tmh = NULL; + } + if (NULL != p->th) + { + GNUNET_TRANSPORT_core_disconnect (p->th); + p->th = NULL; + } + if (NULL != p->peer) + { + if (GNUNET_OK != + GNUNET_TESTING_peer_stop (p->peer)) + { + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Testing lib failed to stop peer %u (`%s')\n", + p->no, + GNUNET_i2s (&p->id)); + } + GNUNET_TESTING_peer_destroy (p->peer); + p->peer = NULL; + } + if (NULL != p->ats) + { + GNUNET_ATS_connectivity_done (p->ats); + p->ats = NULL; + } + if (NULL != p->hello) + { + GNUNET_free (p->hello); + p->hello = NULL; + } + if (NULL != p->cfg) + { + GNUNET_CONFIGURATION_destroy (p->cfg); + p->cfg = NULL; + } + if (NULL != p->handlers) + { + GNUNET_free (p->handlers); + p->handlers = NULL; + } + GNUNET_CONTAINER_DLL_remove (tth->p_head, + tth->p_tail, + p); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Peer %u (`%s') stopped\n", + p->no, + GNUNET_i2s (&p->id)); + GNUNET_free (p); +} + + +/** + * Function called after the HELLO was passed to the + * transport service. + * FIXME maybe schedule the application_validate somehow + */ +/* +static void +hello_offered (void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc = cls; + + cc->oh = NULL; + cc->tct = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &offer_hello, + cc); +}*/ + + +/** + * Offer the current HELLO of P2 to P1. + * + * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectRequest` + */ +static void +offer_hello (void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc = cls; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p1 = cc->p1; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p2 = cc->p2; + struct GNUNET_TIME_Absolute t; + enum GNUNET_NetworkType nt; + char *addr; + + cc->tct = NULL; + { + char *p2_s = GNUNET_strdup (GNUNET_i2s (&p2->id)); + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Asking peer %u (`%s') to connect peer %u (`%s'), providing HELLO with %s\n", + p1->no, + GNUNET_i2s (&p1->id), + p2->no, + p2_s, + cc->p2->hello); + GNUNET_free (p2_s); + } + + addr = GNUNET_HELLO_extract_address (cc->p2->hello, + cc->p2->hello_size, + &cc->p2->id, + &nt, + &t); + GNUNET_assert (NULL == addr); + GNUNET_TRANSPORT_application_validate (cc->p1->ah, + &cc->p2->id, + nt, + addr); + GNUNET_free (addr); +} + + +/** + * Initiate a connection from p1 to p2 by offering p1 p2's HELLO message + * + * Remarks: start_peer's notify_connect callback can be called before. + * + * @param tth transport testing handle + * @param p1 peer 1 + * @param p2 peer 2 + * @param cb the callback to call when both peers notified that they are connected + * @param cls callback cls + * @return a connect request handle + */ +struct GNUNET_TRANSPORT_TESTING_ConnectRequest * +GNUNET_TRANSPORT_TESTING_connect_peers (struct + GNUNET_TRANSPORT_TESTING_PeerContext *p1, + struct + GNUNET_TRANSPORT_TESTING_PeerContext *p2, + GNUNET_SCHEDULER_TaskCallback cb, + void *cls) +{ + struct GNUNET_TRANSPORT_TESTING_Handle *tth = p1->tth; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn; + + ccn = NULL; + for (cc = tth->cc_head; NULL != cc; cc = cc->next) + { + if ((cc->p1 == p1) && + (cc->p2 == p2)) + { + ccn = cc; + break; + } + } + + cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest); + cc->p1 = p1; + cc->p2 = p2; + cc->cb = cb; + if (NULL != cls) + cc->cb_cls = cls; + else + cc->cb_cls = cc; + if (NULL != ccn) + { + cc->p1_c = ccn->p1_c; + cc->p2_c = ccn->p2_c; + cc->connected = ccn->connected; + } + GNUNET_CONTAINER_DLL_insert (tth->cc_head, + tth->cc_tail, + cc); + cc->tct = GNUNET_SCHEDULER_add_now (&offer_hello, + cc); + cc->ats_sh = GNUNET_ATS_connectivity_suggest (cc->p1->ats, + &p2->id, + 1); + LOG (GNUNET_ERROR_TYPE_DEBUG, + "New connect request %p\n", + cc); + return cc; +} + + +/** + * Cancel the request to connect two peers + * Tou MUST cancel the request if you stop the peers before the peers connected succesfully + * + * @param tth transport testing handle + * @param cc a connect request handle + */ +void +GNUNET_TRANSPORT_TESTING_connect_peers_cancel (struct + GNUNET_TRANSPORT_TESTING_ConnectRequest + *cc) +{ + struct GNUNET_TRANSPORT_TESTING_Handle *tth = cc->p1->tth; + + LOG (GNUNET_ERROR_TYPE_DEBUG, + "Canceling connect request!\n"); + if (NULL != cc->tct) + { + GNUNET_SCHEDULER_cancel (cc->tct); + cc->tct = NULL; + } + if (NULL != cc->ats_sh) + { + GNUNET_ATS_connectivity_suggest_cancel (cc->ats_sh); + cc->ats_sh = NULL; + } + GNUNET_CONTAINER_DLL_remove (tth->cc_head, + tth->cc_tail, + cc); + GNUNET_free (cc); +} + + +/** + * Clean up the transport testing + * + * @param tth transport testing handle + */ +void +GNUNET_TRANSPORT_TESTING_done (struct GNUNET_TRANSPORT_TESTING_Handle *tth) +{ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc; + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ct; + struct GNUNET_TRANSPORT_TESTING_PeerContext *p; + struct GNUNET_TRANSPORT_TESTING_PeerContext *t; + + if (NULL == tth) + return; + cc = tth->cc_head; + while (NULL != cc) + { + ct = cc->next; + LOG (GNUNET_ERROR_TYPE_ERROR, + "Developer forgot to cancel connect request!\n"); + GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc); + cc = ct; + } + p = tth->p_head; + while (NULL != p) + { + t = p->next; + LOG (GNUNET_ERROR_TYPE_ERROR, + "Developer forgot to stop peer!\n"); + GNUNET_TRANSPORT_TESTING_stop_peer (p); + p = t; + } + GNUNET_TESTING_system_destroy (tth->tl_system, + GNUNET_YES); + + GNUNET_free (tth); +} + + +/** + * Initialize the transport testing + * + * @return transport testing handle + */ +struct GNUNET_TRANSPORT_TESTING_Handle * +GNUNET_TRANSPORT_TESTING_init () +{ + struct GNUNET_TRANSPORT_TESTING_Handle *tth; + + tth = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_Handle); + tth->tl_system = GNUNET_TESTING_system_create ("transport-testing", + NULL, + NULL, + NULL); + if (NULL == tth->tl_system) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to initialize testing library!\n"); + GNUNET_free (tth); + return NULL; + } + return tth; +} + + +/* end of transport-testing.c */ diff --git a/src/transport/transport-testing2.h b/src/transport/transport-testing2.h new file mode 100644 index 000000000..3554436d8 --- /dev/null +++ b/src/transport/transport-testing2.h @@ -0,0 +1,924 @@ +/* + This file is part of GNUnet. + Copyright (C) 2006, 2009, 2015, 2016 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 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 . + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file transport-testing.h + * @brief testing lib for transport service + * @author Matthias Wachs + * @author Christian Grothoff + */ +#ifndef TRANSPORT_TESTING_H +#define TRANSPORT_TESTING_H +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_hello_lib.h" +#include "gnunet_peerstore_service.h" +#include "gnunet_transport_core_service.h" +#include "gnunet_transport_application_service.h" +#include "gnunet_transport_manipulation_service.h" +#include "gnunet_testing_lib.h" + + +/* ************* Basic functions for starting/stopping/connecting *********** */ + +/** + * Context for a single peer + */ +struct GNUNET_TRANSPORT_TESTING_PeerContext; + +/** + * Definition for a transport testing handle + */ +struct GNUNET_TRANSPORT_TESTING_Handle; + + +/** + * Context for a single peer + */ +struct GNUNET_TRANSPORT_TESTING_PeerContext +{ + /** + * Next element in the DLL + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *next; + + /** + * Previous element in the DLL + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *prev; + + /** + * Transport testing handle this peer belongs to + */ + struct GNUNET_TRANSPORT_TESTING_Handle *tth; + + /** + * Application handle + */ + struct GNUNET_TRANSPORT_ApplicationHandle *ah; + + /** + * Peer's configuration + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Peer's transport service handle + */ + struct GNUNET_TRANSPORT_CoreHandle *th; + + /** + * Peer's transport service manipulation handle + */ + struct GNUNET_TRANSPORT_ManipulationHandle *tmh; + + /** + * Peer's ATS handle. + */ + struct GNUNET_ATS_ConnectivityHandle *ats; + + /** + * Peer's PEERSTORE Handle + */ + struct GNUNET_PEERSTORE_Handle *ph; + + /** + * Peer's transport get hello handle to retrieve peer's HELLO message + */ + struct GNUNET_PEERSTORE_IterateContext *pic; + + /** + * Hello + */ + char *hello; + + /** + * Hello size + */ + size_t hello_size; + + /** + * Peer's testing handle + */ + struct GNUNET_TESTING_Peer *peer; + + /** + * Peer identity + */ + struct GNUNET_PeerIdentity id; + + /** + * Handle for the peer's ARM process + */ + struct GNUNET_OS_Process *arm_proc; + + /** + * Receive callback + */ + struct GNUNET_MQ_MessageHandler *handlers; + + /** + * Notify connect callback + */ + GNUNET_TRANSPORT_NotifyConnect nc; + + /** + * Notify disconnect callback + */ + GNUNET_TRANSPORT_NotifyDisconnect nd; + + /** + * Startup completed callback + */ + GNUNET_SCHEDULER_TaskCallback start_cb; + + /** + * Closure for the @a nc and @a nd callbacks + */ + void *cb_cls; + + /** + * Closure for @e start_cb. + */ + void *start_cb_cls; + + /** + * An unique number to identify the peer + */ + unsigned int no; +}; + + +/** + * Handle for a request to connect two peers. + */ +struct GNUNET_TRANSPORT_TESTING_ConnectRequest +{ + /** + * Kept in a DLL. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *next; + + /** + * Kept in a DLL. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *prev; + + /** + * Peer we want to connect. + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p1; + + /** + * Peer we want to connect. + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p2; + + /** + * Task by which we accomplish the connection. + */ + struct GNUNET_SCHEDULER_Task *tct; + + /** + * Handle by which we ask ATS to faciliate the connection. + */ + struct GNUNET_ATS_ConnectivitySuggestHandle *ats_sh; + + /** + * Function to call upon completion. + */ + GNUNET_SCHEDULER_TaskCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Message queue for sending from @a p1 to @a p2. + */ + struct GNUNET_MQ_Handle *mq; + + /** + * Set if peer1 says the connection is up to peer2. + */ + int p1_c; + + /** + * Set if peer2 says the connection is up to peer1. + */ + int p2_c; + + /** + * #GNUNET_YES if both @e p1_c and @e p2_c are #GNUNET_YES. + */ + int connected; +}; + + +/** + * Handle for a test run. + */ +struct GNUNET_TRANSPORT_TESTING_Handle +{ + /** + * Testing library system handle + */ + struct GNUNET_TESTING_System *tl_system; + + /** + * head DLL of connect contexts + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc_head; + + /** + * head DLL of connect contexts + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc_tail; + + /** + * head DLL of peers + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p_head; + + /** + * tail DLL of peers + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext *p_tail; +}; + + +/** + * Initialize the transport testing + * + * @return transport testing handle + */ +struct GNUNET_TRANSPORT_TESTING_Handle * +GNUNET_TRANSPORT_TESTING_init (void); + + +/** + * Clean up the transport testing + * + * @param tth transport testing handle + */ +void +GNUNET_TRANSPORT_TESTING_done (struct GNUNET_TRANSPORT_TESTING_Handle *tth); + + +/** + * Start a peer with the given configuration + * + * @param tth the testing handle + * @param cfgname configuration file + * @param peer_id the peer_id + * @param handlers functions for receiving messages + * @param nc connect callback + * @param nd disconnect callback + * @param cb_cls closure for @a nc and @a nd callback + * @param start_cb start callback + * @param start_cb_cls closure for @a start_cb + * @return the peer context + */ +struct GNUNET_TRANSPORT_TESTING_PeerContext * +GNUNET_TRANSPORT_TESTING_start_peer ( + struct GNUNET_TRANSPORT_TESTING_Handle *tth, + const char *cfgname, + int peer_id, + const struct GNUNET_MQ_MessageHandler *handlers, + GNUNET_TRANSPORT_NotifyConnect nc, + GNUNET_TRANSPORT_NotifyDisconnect nd, + void *cb_cls, + GNUNET_SCHEDULER_TaskCallback start_cb, + void *start_cb_cls); + + +/** + * Shutdown the given peer + * + * @param p the peer + */ +void +GNUNET_TRANSPORT_TESTING_stop_peer ( + struct GNUNET_TRANSPORT_TESTING_PeerContext *pc); + + +/** + * Stops and restarts the given peer, sleeping (!) for 5s in between. + * + * @param p the peer + * @param restart_cb restart callback + * @param restart_cb_cls callback closure + * @return #GNUNET_OK in success otherwise #GNUNET_SYSERR + */ +int +GNUNET_TRANSPORT_TESTING_restart_peer ( + struct GNUNET_TRANSPORT_TESTING_PeerContext *p, + GNUNET_SCHEDULER_TaskCallback restart_cb, + void *restart_cb_cls); + + +/** + * Connect the given peers and call the callback when both peers + * report the inbound connection. Remarks: start_peer's notify_connect + * callback can be called before. + * + * @param p1 peer 1 + * @param p2 peer 2 + * @param cb the callback to call when both peers notified that they are + * connected + * @param cls callback cls + * @return a connect request handle + */ +struct GNUNET_TRANSPORT_TESTING_ConnectRequest * +GNUNET_TRANSPORT_TESTING_connect_peers ( + struct GNUNET_TRANSPORT_TESTING_PeerContext *p1, + struct GNUNET_TRANSPORT_TESTING_PeerContext *p2, + GNUNET_SCHEDULER_TaskCallback cb, + void *cls); + + +/** + * Cancel the request to connect two peers. You MUST cancel the + * request if you stop the peers before the peers connected + * succesfully. + * + * @param cc a connect request handle + */ +void +GNUNET_TRANSPORT_TESTING_connect_peers_cancel ( + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc); + + +/** + * Function called on matching connect requests. + * + * @param cls closure + * @param cc request matching the query + */ +typedef void (*GNUNET_TRANSPORT_TESTING_ConnectContextCallback) ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc); + + +/** + * Find any connecting context matching the given pair of peers. + * + * @param p1 first peer + * @param p2 second peer + * @param cb function to call + * @param cb_cls closure for @a cb + */ +void +GNUNET_TRANSPORT_TESTING_find_connecting_context ( + struct GNUNET_TRANSPORT_TESTING_PeerContext *p1, + struct GNUNET_TRANSPORT_TESTING_PeerContext *p2, + GNUNET_TRANSPORT_TESTING_ConnectContextCallback cb, + void *cb_cls); + + +/* ********************** high-level process functions *************** */ + + +/** + * Function called once the peers have been launched and + * connected by #GNUNET_TRANSPORT_TESTING_connect_check(). + * + * @param cls closure + * @param num_peers size of the @a p array + * @param p the peers that were launched + */ +typedef void (*GNUNET_TRANSPORT_TESTING_ConnectContinuation) ( + void *cls, + unsigned int num_peers, + struct GNUNET_TRANSPORT_TESTING_PeerContext *p[]); + + +/** + * Internal data structure. + */ +struct GNUNET_TRANSPORT_TESTING_ConnectRequestList; + +/** + * Internal data structure. + */ +struct GNUNET_TRANSPORT_TESTING_InternalPeerContext; + + +GNUNET_NETWORK_STRUCT_BEGIN +struct GNUNET_TRANSPORT_TESTING_TestMessage +{ + /** + * Type is (usually) #GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE. + */ + struct GNUNET_MessageHeader header; + + /** + * Monotonically increasing counter throughout the test. + */ + uint32_t num GNUNET_PACKED; +}; +GNUNET_NETWORK_STRUCT_END + + +/** + * Function called by the transport for each received message. + * + * @param cls closure + * @param receiver receiver of the message + * @param sender sender of the message + * @param message the message + */ +typedef void (*GNUNET_TRANSPORT_TESTING_ReceiveCallback) ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, + const struct GNUNET_PeerIdentity *sender, + const struct GNUNET_TRANSPORT_TESTING_TestMessage *message); + + +/** + * Function called to notify transport users that another + * peer connected to us. + * + * @param cls closure + * @param me peer experiencing the event + * @param other peer that connected to @a me + */ +typedef void (*GNUNET_TRANSPORT_TESTING_NotifyConnect) ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *me, + const struct GNUNET_PeerIdentity *other); + + +/** + * Function called to notify transport users that another + * peer disconnected from us. + * + * @param cls closure + * @param me peer experiencing the event + * @param other peer that disconnected from @a me + */ +typedef void (*GNUNET_TRANSPORT_TESTING_NotifyDisconnect) ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *me, + const struct GNUNET_PeerIdentity *other); + + +/** + * Closure that must be passed to + * #GNUNET_TRANSPORT_TESTING_connect_check. + */ +struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext +{ + /** + * How should we continue after the connect? + */ + GNUNET_SCHEDULER_TaskCallback connect_continuation; + + /** + * Closure for @e connect_continuation. + */ + void *connect_continuation_cls; + + /** + * Which configuration file should we pass to the + * #GNUNET_PROGRAM_run() of the testcase? + */ + const char *config_file; + + /** + * Receiver argument to give for peers we start. + */ + GNUNET_TRANSPORT_TESTING_ReceiveCallback rec; + + /** + * Notify connect argument to give for peers we start. + */ + GNUNET_TRANSPORT_TESTING_NotifyConnect nc; + + /** + * Notify disconnect argument to give for peers we start. + */ + GNUNET_TRANSPORT_TESTING_NotifyDisconnect nd; + + /** + * Closure for @e rec, @e nc and @e nd. + */ + void *cls; + + /** + * Custom task to run on shutdown. + */ + GNUNET_SCHEDULER_TaskCallback shutdown_task; + + /** + * Closure for @e shutdown_task. + */ + void *shutdown_task_cls; + + /** + * Custom task to run after peers were started but before we try to + * connect them. If this function is set, we wait ONE second after + * running this function until we continue with connecting the + * peers. + */ + GNUNET_SCHEDULER_TaskCallback pre_connect_task; + + /** + * Closure for @e shutdown_task. + */ + void *pre_connect_task_cls; + + /** + * When should the testcase time out? + */ + struct GNUNET_TIME_Relative timeout; + + /** + * Should we try to create connections in both directions? + */ + int bi_directional; + + /* ******* fields set by #GNUNET_TRANSPORT_TESTING_connect_check **** */ + + /** + * Number of peers involved in the test. + */ + unsigned int num_peers; + + /** + * Configuration files we have, array with @e num_peers entries. + */ + char **cfg_files; + + /** + * Array with @e num_peers entries. + */ + struct GNUNET_TRANSPORT_TESTING_PeerContext **p; + + /** + * Name of the plugin. + */ + const char *test_plugin; + + /** + * Name of the testcase. + */ + const char *test_name; + + /** + * Configuration object for the testcase. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Main testing handle. + */ + struct GNUNET_TRANSPORT_TESTING_Handle *tth; + + /** + * Result from the main function, set to #GNUNET_OK on success. + * Clients should set to #GNUNET_SYSERR to indicate test failure. + */ + int global_ret; + + /** + * Generator for the `num` field in test messages. Incremented each + * time #GNUNET_TRANSPORT_TESTING_simple_send or + * #GNUNET_TRANSPORT_TESTING_large_send are used to transmit a + * message. + */ + uint32_t send_num_gen; + + /* ******* internal state, clients should not mess with this **** */ + + /** + * Task run on timeout. + */ + struct GNUNET_SCHEDULER_Task *timeout_task; + + /** + * Task run to connect peers. + */ + struct GNUNET_SCHEDULER_Task *connect_task; + + /** + * Number of peers that have been started. + */ + unsigned int started; + + /** + * DLL of active connect requests. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl_head; + + /** + * DLL of active connect requests. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectRequestList *crl_tail; + + /** + * Array with @e num_peers entries. + */ + struct GNUNET_TRANSPORT_TESTING_InternalPeerContext *ip; +}; + + +/** + * Find peer by peer ID. + * + * @param ccc context to search + * @param peer peer to look for + * @return NULL if @a peer was not found + */ +struct GNUNET_TRANSPORT_TESTING_PeerContext * +GNUNET_TRANSPORT_TESTING_find_peer ( + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc, + const struct GNUNET_PeerIdentity *peer); + + +/** + * Common implementation of the #GNUNET_TRANSPORT_TESTING_CheckCallback. + * Starts and connects the two peers, then invokes the + * `connect_continuation` from @a cls. Sets up a timeout to + * abort the test, and a shutdown handler to clean up properly + * on exit. + * + * @param cls closure of type `struct + * GNUNET_TRANSPORT_TESTING_ConnectCheckContext` + * @param tth_ initialized testing handle + * @param test_plugin_ name of the plugin + * @param test_name_ name of the test + * @param num_peers number of entries in the @a cfg_file array + * @param cfg_files array of names of configuration files for the peers + * @return #GNUNET_SYSERR on error + */ +int +GNUNET_TRANSPORT_TESTING_connect_check ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_Handle *tth_, + const char *test_plugin_, + const char *test_name_, + unsigned int num_peers, + char *cfg_files[]); + + +/** + * Main function of a testcase. Called with the initial setup data + * for the test as derived from the source name and the binary name. + * + * @param cls closure + * @param tth_ initialized testing handle + * @param test_plugin_ name of the plugin + * @param test_name_ name of the test + * @param num_peers number of entries in the @a cfg_file array + * @param cfg_files array of names of configuration files for the peers + * @return #GNUNET_SYSERR on error + */ +typedef int (*GNUNET_TRANSPORT_TESTING_CheckCallback) ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_Handle *tth_, + const char *test_plugin_, + const char *test_name_, + unsigned int num_peers, + char *cfg_files[]); + + +/** + * Setup testcase. Calls @a check with the data the test needs. + * + * @param argv0 binary name (argv[0]) + * @param filename source file name (__FILE__) + * @param num_peers number of peers to start + * @param check main function to run + * @param check_cls closure for @a check + * @return #GNUNET_OK on success + */ +int +GNUNET_TRANSPORT_TESTING_main_ (const char *argv0, + const char *filename, + unsigned int num_peers, + GNUNET_TRANSPORT_TESTING_CheckCallback check, + void *check_cls); + + +/** + * Setup testcase. Calls @a check with the data the test needs. + * + * @param num_peers number of peers to start + * @param check main function to run + * @param check_cls closure for @a check + * @return #GNUNET_OK on success + */ +#define GNUNET_TRANSPORT_TESTING_main(num_peers, check, check_cls) \ + GNUNET_TRANSPORT_TESTING_main_ (argv[0], \ + __FILE__, \ + num_peers, \ + check, \ + check_cls) + +/* ***************** Convenience functions for sending ********* */ + +/** + * Send a test message of type @a mtype and size @a msize from + * peer @a sender to peer @a receiver. The peers should be + * connected when this function is called. + * + * @param sender the sending peer + * @param receiver the receiving peer + * @param mtype message type to use + * @param msize size of the message, at least `sizeof (struct + * GNUNET_TRANSPORT_TESTING_TestMessage)` + * @param num unique message number + * @param cont continuation to call after transmission + * @param cont_cls closure for @a cont + * @return #GNUNET_OK if message was queued, + * #GNUNET_NO if peers are not connected + * #GNUNET_SYSERR if @a msize is illegal + */ +int +GNUNET_TRANSPORT_TESTING_send ( + struct GNUNET_TRANSPORT_TESTING_PeerContext *sender, + struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver, + uint16_t mtype, + uint16_t msize, + uint32_t num, + GNUNET_SCHEDULER_TaskCallback cont, + void *cont_cls); + + +/** + * Message type used by #GNUNET_TRANSPORT_TESTING_simple_send(). + */ +#define GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE 12345 + +/** + * Alternative message type for tests. + */ +#define GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE2 12346 + + +/** + * Type of the closure argument to pass to + * #GNUNET_TRANSPORT_TESTING_simple_send() and + * #GNUNET_TRANSPORT_TESTING_large_send(). + */ +struct GNUNET_TRANSPORT_TESTING_SendClosure +{ + /** + * Context for the transmission. + */ + struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc; + + /** + * Function that returns the desired message size. Overrides + * the message size, can be NULL in which case the message + * size is the default. + */ + size_t (*get_size_cb) (unsigned int n); + + /** + * Number of messages to be transmitted in a loop. + * Use zero for "forever" (until external shutdown). + */ + unsigned int num_messages; + + /** + * Function to call after all transmissions, can be NULL. + */ + GNUNET_SCHEDULER_TaskCallback cont; + + /** + * Closure for @e cont. + */ + void *cont_cls; +}; + + +/** + * Task that sends a minimalistic test message from the + * first peer to the second peer. + * + * @param cls the `struct GNUNET_TRANSPORT_TESTING_SendClosure` + * which should contain at least two peers, the first two + * of which should be currently connected + */ +void +GNUNET_TRANSPORT_TESTING_simple_send (void *cls); + +/** + * Size of a message sent with + * #GNUNET_TRANSPORT_TESTING_large_send(). Big enough + * to usually force defragmentation. + */ +#define GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE 2600 + +/** + * Task that sends a large test message from the + * first peer to the second peer. + * + * @param cls the `struct GNUNET_TRANSPORT_TESTING_SendClosure` + * which should contain at least two peers, the first two + * of which should be currently connected + */ +void +GNUNET_TRANSPORT_TESTING_large_send (void *cls); + + +/* ********************** log-only convenience functions ************* */ + + +/** + * Log a connect event. + * + * @param cls NULL + * @param me peer that had the event + * @param other peer that connected. + */ +void +GNUNET_TRANSPORT_TESTING_log_connect ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *me, + const struct GNUNET_PeerIdentity *other); + + +/** + * Log a disconnect event. + * + * @param cls NULL + * @param me peer that had the event + * @param other peer that disconnected. + */ +void +GNUNET_TRANSPORT_TESTING_log_disconnect ( + void *cls, + struct GNUNET_TRANSPORT_TESTING_PeerContext *me, + const struct GNUNET_PeerIdentity *other); + + +/* ********************** low-level filename functions *************** */ + + +/** + * Extracts the test filename from an absolute file name and removes + * the extension. + * + * @param file absolute file name + * @return resulting test name + */ +char * +GNUNET_TRANSPORT_TESTING_get_test_name (const char *file); + + +/** + * This function takes the filename (e.g. argv[0), removes a "lt-"-prefix and + * if existing ".exe"-prefix and adds the peer-number + * + * @param file filename of the test, e.g. argv[0] + * @param count peer number + * @return configuration name to use + */ +char * +GNUNET_TRANSPORT_TESTING_get_config_name (const char *file, int count); + + +/** + * Extracts the plugin anme from an absolute file name and the test name + * @param file absolute file name + * @param test test name + * @return the plugin name + */ +char * +GNUNET_TRANSPORT_TESTING_get_test_plugin_name (const char *executable, + const char *testname); + + +/** + * Extracts the filename from an absolute file name and removes the + * extenstion + * + * @param file absolute file name + * @return the source name + */ +char * +GNUNET_TRANSPORT_TESTING_get_test_source_name (const char *file); + +#endif +/* end of transport_testing.h */ -- cgit v1.2.3 From ddbb4c740614d988431700bdcbb358de00be6313 Mon Sep 17 00:00:00 2001 From: Martin Schanzenbach Date: Mon, 30 Nov 2020 19:14:20 +0900 Subject: -fix link --- src/transport/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/src/transport/Makefile.am b/src/transport/Makefile.am index 605d89cc0..800a27aa2 100644 --- a/src/transport/Makefile.am +++ b/src/transport/Makefile.am @@ -175,6 +175,7 @@ libgnunettransporttesting2_la_LIBADD = \ libgnunettransportapplication.la \ libgnunettransportcore.la \ $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/ats/libgnunetats.la \ $(top_builddir)/src/hello/libgnunethello.la \ $(top_builddir)/src/peerstore/libgnunetpeerstore.la \ $(top_builddir)/src/util/libgnunetutil.la -- cgit v1.2.3