1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819 |
- /*
- This file is part of GNUnet.
- Copyright (C) 2010-2015 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 <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file transport/gnunet-service-transport_validation.c
- * @brief address validation subsystem
- * @author Christian Grothoff
- */
- #include "platform.h"
- #include "gnunet-service-transport_ats.h"
- #include "gnunet-service-transport_hello.h"
- #include "gnunet-service-transport_neighbours.h"
- #include "gnunet-service-transport_plugins.h"
- #include "gnunet-service-transport_validation.h"
- #include "gnunet-service-transport.h"
- #include "gnunet_hello_lib.h"
- #include "gnunet_ats_service.h"
- #include "gnunet_peerinfo_service.h"
- #include "gnunet_signatures.h"
- /**
- * Current state of a validation process.
- *
- * FIXME: what state is used to indicate that a validation
- * was successful? If that is clarified/determined, "UGH" in
- * ~gnunetpeerinfogtk.c:1103 should be resolved.
- */
- enum GNUNET_TRANSPORT_ValidationState
- {
- /**
- * Undefined state
- *
- * Used for final callback indicating operation done
- */
- GNUNET_TRANSPORT_VS_NONE,
- /**
- * Fresh validation entry
- *
- * Entry was just created, no validation process was executed
- */
- GNUNET_TRANSPORT_VS_NEW,
- /**
- * Updated validation entry
- *
- * This is an update for an existing validation entry
- */
- GNUNET_TRANSPORT_VS_UPDATE,
- /**
- * Timeout for validation entry
- *
- * A timeout occured during the validation process
- */
- GNUNET_TRANSPORT_VS_TIMEOUT,
- /**
- * Validation entry is removed
- *
- * The validation entry is getting removed due to a failed validation
- */
- GNUNET_TRANSPORT_VS_REMOVE
- };
- /**
- * How long is a PONG signature valid? We'll recycle a signature until
- * 1/4 of this time is remaining. PONGs should expire so that if our
- * external addresses change an adversary cannot replay them indefinitely.
- * OTOH, we don't want to spend too much time generating PONG signatures,
- * so they must have some lifetime to reduce our CPU usage.
- */
- #define PONG_SIGNATURE_LIFETIME GNUNET_TIME_relative_multiply ( \
- GNUNET_TIME_UNIT_HOURS, 1)
- /**
- * After how long do we expire an address in a HELLO that we just
- * validated? This value is also used for our own addresses when we
- * create a HELLO.
- */
- #define HELLO_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply ( \
- GNUNET_TIME_UNIT_HOURS, 12)
- /**
- * How often do we allow PINGing an address that we have not yet
- * validated? This also determines how long we track an address that
- * we cannot validate (because after this time we can destroy the
- * validation record).
- */
- #define UNVALIDATED_PING_KEEPALIVE GNUNET_TIME_relative_multiply ( \
- GNUNET_TIME_UNIT_MINUTES, 5)
- /**
- * How often do we PING an address that we have successfully validated
- * in the past but are not actively using? Should be (significantly)
- * smaller than HELLO_ADDRESS_EXPIRATION.
- */
- #define VALIDATED_PING_FREQUENCY GNUNET_TIME_relative_multiply ( \
- GNUNET_TIME_UNIT_MINUTES, 15)
- /**
- * How often do we PING an address that we are currently using?
- */
- #define CONNECTED_PING_FREQUENCY GNUNET_TIME_relative_multiply ( \
- GNUNET_TIME_UNIT_MINUTES, 2)
- /**
- * How much delay is acceptable for sending the PING or PONG?
- */
- #define ACCEPTABLE_PING_DELAY GNUNET_TIME_relative_multiply ( \
- GNUNET_TIME_UNIT_SECONDS, 1)
- /**
- * Size of the validation map hashmap.
- */
- #define VALIDATION_MAP_SIZE 256
- /**
- * Priority to use for PINGs
- */
- #define PING_PRIORITY 2
- /**
- * Priority to use for PONGs
- */
- #define PONG_PRIORITY 4
- GNUNET_NETWORK_STRUCT_BEGIN
- /**
- * Message used to ask a peer to validate receipt (to check an address
- * from a HELLO). Followed by the address we are trying to validate,
- * or an empty address if we are just sending a PING to confirm that a
- * connection which the receiver (of the PING) initiated is still valid.
- */
- struct TransportPingMessage
- {
- /**
- * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_PING
- */
- struct GNUNET_MessageHeader header;
- /**
- * Challenge code (to ensure fresh reply).
- */
- uint32_t challenge GNUNET_PACKED;
- /**
- * Who is the intended recipient?
- */
- struct GNUNET_PeerIdentity target;
- };
- /**
- * Message used to validate a HELLO. The challenge is included in the
- * confirmation to make matching of replies to requests possible. The
- * signature signs our public key, an expiration time and our address.<p>
- *
- * This message is followed by our transport address that the PING tried
- * to confirm (if we liked it). The address can be empty (zero bytes)
- * if the PING had not address either (and we received the request via
- * a connection that we initiated).
- */
- struct TransportPongMessage
- {
- /**
- * Type will be #GNUNET_MESSAGE_TYPE_TRANSPORT_PONG
- */
- struct GNUNET_MessageHeader header;
- /**
- * Challenge code from PING (showing freshness). Not part of what
- * is signed so that we can re-use signatures.
- */
- uint32_t challenge GNUNET_PACKED;
- /**
- * Signature.
- */
- struct GNUNET_CRYPTO_EddsaSignature signature;
- /**
- * #GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN to confirm that this is a
- * plausible address for the signing peer.
- */
- struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
- /**
- * When does this signature expire?
- */
- struct GNUNET_TIME_AbsoluteNBO expiration;
- /**
- * Size of address appended to this message (part of what is
- * being signed, hence not redundant).
- */
- uint32_t addrlen GNUNET_PACKED;
- };
- GNUNET_NETWORK_STRUCT_END
- /**
- * Information about an address under validation
- */
- struct ValidationEntry
- {
- /**
- * The address.
- */
- struct GNUNET_HELLO_Address *address;
- /**
- * Handle to the blacklist check (if we're currently in it).
- */
- struct GST_BlacklistCheck *bc;
- /**
- * Cached PONG signature
- */
- struct GNUNET_CRYPTO_EddsaSignature pong_sig_cache;
- /**
- * ID of task that will clean up this entry if nothing happens.
- */
- struct GNUNET_SCHEDULER_Task *timeout_task;
- /**
- * ID of task that will trigger address revalidation.
- */
- struct GNUNET_SCHEDULER_Task *revalidation_task;
- /**
- * At what time did we send the latest validation request (PING)?
- */
- struct GNUNET_TIME_Absolute send_time;
- /**
- * At what time do we send the next validation request (PING)?
- */
- struct GNUNET_TIME_Absolute next_validation;
- /**
- * Until when is this address valid?
- * ZERO if it is not currently considered valid.
- */
- struct GNUNET_TIME_Absolute valid_until;
- /**
- * Until when is the cached PONG signature valid?
- * ZERO if it is not currently considered valid.
- */
- struct GNUNET_TIME_Absolute pong_sig_valid_until;
- /**
- * How long until we can try to validate this address again?
- * FOREVER if the address is for an unsupported plugin (from PEERINFO)
- * ZERO if the address is considered valid (no validation needed)
- * otherwise a time in the future if we're currently denying re-validation
- */
- struct GNUNET_TIME_Absolute revalidation_block;
- /**
- * Last observed latency for this address (round-trip), delay between
- * last PING sent and PONG received; FOREVER if we never got a PONG.
- */
- struct GNUNET_TIME_Relative latency;
- /**
- * Current state of this validation entry
- */
- enum GNUNET_TRANSPORT_ValidationState state;
- /**
- * Challenge number we used.
- */
- uint32_t challenge;
- /**
- * When passing the address in #add_valid_peer_address(), did we
- * copy the address to the HELLO yet?
- */
- int copied;
- /**
- * Are we currently using this address for a connection?
- */
- int in_use;
- /**
- * Are we expecting a PONG message for this validation entry?
- */
- int expecting_pong;
- /**
- * Is this address known to ATS as valid right now?
- */
- int known_to_ats;
- /**
- * Which network type does our address belong to?
- */
- enum GNUNET_NetworkType network;
- };
- /**
- * Map of PeerIdentities to 'struct ValidationEntry*'s (addresses
- * of the given peer that we are currently validating, have validated
- * or are blocked from re-validation for a while).
- */
- static struct GNUNET_CONTAINER_MultiPeerMap *validation_map;
- /**
- * Context for peerinfo iteration.
- */
- static struct GNUNET_PEERINFO_NotifyContext *pnc;
- /**
- * Minimum delay between to validations
- */
- static struct GNUNET_TIME_Relative validation_delay;
- /**
- * Number of validations running; any PING that was not yet
- * matched by a PONG and for which we have not yet hit the
- * timeout is considered a running 'validation'.
- */
- static unsigned int validations_running;
- /**
- * Validition fast start threshold
- */
- static unsigned int validations_fast_start_threshold;
- /**
- * When is next validation allowed
- */
- static struct GNUNET_TIME_Absolute validation_next;
- /**
- * Context for the validation entry match function.
- */
- struct ValidationEntryMatchContext
- {
- /**
- * Where to store the result?
- */
- struct ValidationEntry *ve;
- /**
- * Address we're interested in.
- */
- const struct GNUNET_HELLO_Address *address;
- };
- /**
- * Provide an update on the `validation_map` map size to statistics.
- * This function should be called whenever the `validation_map`
- * is changed.
- */
- static void
- publish_ve_stat_update ()
- {
- GNUNET_STATISTICS_set (GST_stats,
- gettext_noop ("# Addresses in validation map"),
- GNUNET_CONTAINER_multipeermap_size (validation_map),
- GNUNET_NO);
- }
- /**
- * Iterate over validation entries until a matching one is found.
- *
- * @param cls the `struct ValidationEntryMatchContext *`
- * @param key peer identity (unused)
- * @param value a `struct ValidationEntry *` to match
- * @return #GNUNET_YES if the entry does not match,
- * #GNUNET_NO if the entry does match
- */
- static int
- validation_entry_match (void *cls,
- const struct GNUNET_PeerIdentity *key,
- void *value)
- {
- struct ValidationEntryMatchContext *vemc = cls;
- struct ValidationEntry *ve = value;
- if (0 == GNUNET_HELLO_address_cmp (ve->address,
- vemc->address))
- {
- vemc->ve = ve;
- return GNUNET_NO;
- }
- return GNUNET_YES;
- }
- /**
- * A validation entry changed. Update the state and notify
- * monitors.
- *
- * @param ve validation entry that changed
- * @param state new state
- */
- static void
- validation_entry_changed (struct ValidationEntry *ve,
- enum GNUNET_TRANSPORT_ValidationState state)
- {
- ve->state = state;
- }
- /**
- * Iterate over validation entries and free them.
- *
- * @param cls (unused)
- * @param key peer identity (unused)
- * @param value a `struct ValidationEntry *` to clean up
- * @return #GNUNET_YES (continue to iterate)
- */
- static int
- cleanup_validation_entry (void *cls,
- const struct GNUNET_PeerIdentity *key,
- void *value)
- {
- struct ValidationEntry *ve = value;
- ve->next_validation = GNUNET_TIME_UNIT_ZERO_ABS;
- ve->valid_until = GNUNET_TIME_UNIT_ZERO_ABS;
- /* Notify about deleted entry */
- validation_entry_changed (ve,
- GNUNET_TRANSPORT_VS_REMOVE);
- if (NULL != ve->bc)
- {
- GST_blacklist_test_cancel (ve->bc);
- ve->bc = NULL;
- }
- GNUNET_break (GNUNET_OK ==
- GNUNET_CONTAINER_multipeermap_remove (validation_map,
- &ve->address->peer,
- ve));
- publish_ve_stat_update ();
- if (GNUNET_YES == ve->known_to_ats)
- {
- GST_ats_expire_address (ve->address);
- GNUNET_assert (GNUNET_NO ==
- GST_ats_is_known_no_session (ve->address));
- ve->known_to_ats = GNUNET_NO;
- }
- GNUNET_HELLO_address_free (ve->address);
- if (NULL != ve->timeout_task)
- {
- GNUNET_SCHEDULER_cancel (ve->timeout_task);
- ve->timeout_task = NULL;
- }
- if (NULL != ve->revalidation_task)
- {
- GNUNET_SCHEDULER_cancel (ve->revalidation_task);
- ve->revalidation_task = NULL;
- }
- if ((GNUNET_YES == ve->expecting_pong) &&
- (validations_running > 0))
- {
- validations_running--;
- GNUNET_STATISTICS_set (GST_stats,
- gettext_noop ("# validations running"),
- validations_running,
- GNUNET_NO);
- }
- GNUNET_free (ve);
- return GNUNET_OK;
- }
- /**
- * Address validation cleanup task. Assesses if the record is no
- * longer valid and then possibly triggers its removal.
- *
- * @param cls the `struct ValidationEntry`
- */
- static void
- timeout_hello_validation (void *cls)
- {
- struct ValidationEntry *ve = cls;
- struct GNUNET_TIME_Absolute max;
- struct GNUNET_TIME_Relative left;
- ve->timeout_task = NULL;
- /* For valid addresses, we want to wait until the expire;
- for addresses under PING validation, we want to wait
- until we give up on the PING */
- max = GNUNET_TIME_absolute_max (ve->valid_until,
- ve->revalidation_block);
- left = GNUNET_TIME_absolute_get_remaining (max);
- if (left.rel_value_us > 0)
- {
- /* We should wait a bit longer. This happens when
- address lifetimes are extended due to successful
- validations. */
- ve->timeout_task =
- GNUNET_SCHEDULER_add_delayed (left,
- &timeout_hello_validation,
- ve);
- return;
- }
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop (
- "# address records discarded (timeout)"),
- 1,
- GNUNET_NO);
- cleanup_validation_entry (NULL,
- &ve->address->peer,
- ve);
- }
- /**
- * Function called with the result from blacklisting.
- * Send a PING to the other peer if a communication is allowed.
- *
- * @param cls our `struct ValidationEntry`
- * @param pid identity of the other peer
- * @param address_null address associated with the request, always NULL
- * @param session_null session associated with the request, always NULL
- * @param result #GNUNET_OK if the connection is allowed,
- * #GNUNET_NO if not,
- * #GNUNET_SYSERR if operation was aborted
- */
- static void
- transmit_ping_if_allowed (void *cls,
- const struct GNUNET_PeerIdentity *pid,
- const struct GNUNET_HELLO_Address *address_null,
- struct GNUNET_ATS_Session *session_null,
- int result)
- {
- struct ValidationEntry *ve = cls;
- struct TransportPingMessage ping;
- struct GNUNET_TRANSPORT_PluginFunctions *papi;
- struct GNUNET_TIME_Absolute next;
- const struct GNUNET_MessageHeader *hello;
- ssize_t ret;
- size_t tsize;
- size_t slen;
- uint16_t hsize;
- struct GNUNET_ATS_Session *session;
- ve->bc = NULL;
- if (GNUNET_OK != result)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Blacklist denies sending PING to `%s' `%s' `%s'\n",
- GNUNET_i2s (pid),
- GST_plugins_a2s (ve->address),
- ve->address->transport_name);
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop (
- "# address records discarded (blacklist)"),
- 1,
- GNUNET_NO);
- cleanup_validation_entry (NULL,
- pid,
- ve);
- return;
- }
- hello = GST_hello_get ();
- GNUNET_assert (NULL != hello);
- slen = strlen (ve->address->transport_name) + 1;
- hsize = ntohs (hello->size);
- tsize = sizeof(struct TransportPingMessage)
- + ve->address->address_length + slen + hsize;
- ping.header.size =
- htons (sizeof(struct TransportPingMessage)
- + ve->address->address_length + slen);
- ping.header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PING);
- ping.challenge = htonl (ve->challenge);
- ping.target = *pid;
- if (tsize >= GNUNET_MAX_MESSAGE_SIZE)
- {
- GNUNET_break (0);
- hsize = 0;
- tsize =
- sizeof(struct TransportPingMessage) + ve->address->address_length
- + slen + hsize;
- }
- {
- char message_buf[tsize] GNUNET_ALIGN;
- GNUNET_memcpy (message_buf,
- hello,
- hsize);
- GNUNET_memcpy (&message_buf[hsize],
- &ping,
- sizeof(struct TransportPingMessage));
- GNUNET_memcpy (&message_buf[sizeof(struct TransportPingMessage) + hsize],
- ve->address->transport_name,
- slen);
- GNUNET_memcpy (&message_buf[sizeof(struct TransportPingMessage) + slen
- + hsize],
- ve->address->address,
- ve->address->address_length);
- papi = GST_plugins_find (ve->address->transport_name);
- GNUNET_assert (NULL != papi);
- session = papi->get_session (papi->cls,
- ve->address);
- if (NULL == session)
- {
- /* Could not get a valid session */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Failed to get session to send PING to `%s' at `%s'\n",
- GNUNET_i2s (pid),
- GST_plugins_a2s (ve->address));
- return;
- }
- ret = papi->send (papi->cls, session,
- message_buf, tsize,
- PING_PRIORITY,
- ACCEPTABLE_PING_DELAY,
- NULL, NULL);
- if (-1 == ret)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Failed to send PING to `%s' at `%s'\n",
- GNUNET_i2s (pid),
- GST_plugins_a2s (ve->address));
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Transmitted plain PING to `%s' `%s' `%s'\n",
- GNUNET_i2s (pid),
- GST_plugins_a2s (ve->address),
- ve->address->transport_name);
- ve->network = papi->get_network (papi->cls,
- session);
- GNUNET_break (GNUNET_NT_UNSPECIFIED != ve->network);
- GST_neighbours_notify_data_sent (ve->address,
- session,
- tsize);
- next = GNUNET_TIME_relative_to_absolute (validation_delay);
- validation_next = GNUNET_TIME_absolute_max (next,
- validation_next);
- ve->send_time = GNUNET_TIME_absolute_get ();
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop (
- "# PINGs for address validation sent"),
- 1,
- GNUNET_NO);
- ve->expecting_pong = GNUNET_YES;
- validations_running++;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Validation started, %u validation processes running\n",
- validations_running);
- GNUNET_STATISTICS_set (GST_stats,
- gettext_noop ("# validations running"),
- validations_running,
- GNUNET_NO);
- /* Notify about PING sent */
- validation_entry_changed (ve,
- GNUNET_TRANSPORT_VS_UPDATE);
- }
- }
- /**
- * Do address validation again to keep address valid.
- *
- * @param cls the `struct ValidationEntry`
- */
- static void
- revalidate_address (void *cls)
- {
- struct ValidationEntry *ve = cls;
- struct GNUNET_TIME_Relative canonical_delay;
- struct GNUNET_TIME_Relative delay;
- struct GNUNET_TIME_Relative blocked_for;
- struct GST_BlacklistCheck *bc;
- uint32_t rdelay;
- ve->revalidation_task = NULL;
- delay = GNUNET_TIME_absolute_get_remaining (ve->revalidation_block);
- /* Considering current connectivity situation, what is the maximum
- block period permitted? */
- if (GNUNET_YES == ve->in_use)
- canonical_delay = CONNECTED_PING_FREQUENCY;
- else if (GNUNET_TIME_absolute_get_remaining (ve->valid_until).rel_value_us >
- 0)
- canonical_delay = VALIDATED_PING_FREQUENCY;
- else
- canonical_delay = UNVALIDATED_PING_KEEPALIVE;
- /* Use delay that is MIN of original delay and possibly adjusted
- new maximum delay (which may be lower); the real delay
- is originally randomized between "canonical_delay" and "2 * canonical_delay",
- so continue to permit that window for the operation. */
- delay = GNUNET_TIME_relative_min (delay,
- GNUNET_TIME_relative_multiply (
- canonical_delay,
- 2));
- ve->revalidation_block = GNUNET_TIME_relative_to_absolute (delay);
- if (delay.rel_value_us > 0)
- {
- /* should wait a bit longer */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Waiting for %s longer before (re)validating address `%s'\n",
- GNUNET_STRINGS_relative_time_to_string (delay,
- GNUNET_YES),
- GST_plugins_a2s (ve->address));
- ve->revalidation_task =
- GNUNET_SCHEDULER_add_delayed (delay,
- &revalidate_address, ve);
- ve->next_validation = GNUNET_TIME_relative_to_absolute (delay);
- return;
- }
- /* check if globally we have too many active validations at a
- too high rate, if so, delay ours */
- blocked_for = GNUNET_TIME_absolute_get_remaining (validation_next);
- if ((validations_running > validations_fast_start_threshold) &&
- (blocked_for.rel_value_us > 0))
- {
- /* Validations are blocked, have to wait for blocked_for time */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Validations blocked for another %s, delaying validating address `%s'\n",
- GNUNET_STRINGS_relative_time_to_string (blocked_for,
- GNUNET_YES),
- GST_plugins_a2s (ve->address));
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop (
- "# validations delayed by global throttle"),
- 1,
- GNUNET_NO);
- ve->revalidation_task =
- GNUNET_SCHEDULER_add_delayed (blocked_for,
- &revalidate_address,
- ve);
- ve->next_validation = GNUNET_TIME_relative_to_absolute (blocked_for);
- return;
- }
- /* We are good to go; remember to not go again for `canonical_delay` time;
- add up to `canonical_delay` to randomize start time */
- ve->revalidation_block = GNUNET_TIME_relative_to_absolute (canonical_delay);
- /* schedule next PINGing with some extra random delay to avoid synchronous re-validations */
- rdelay =
- GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
- canonical_delay.rel_value_us);
- delay = GNUNET_TIME_relative_add (canonical_delay,
- GNUNET_TIME_relative_multiply
- (GNUNET_TIME_UNIT_MICROSECONDS,
- rdelay));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Validating now, next scheduled for %s, now validating address `%s'\n",
- GNUNET_STRINGS_relative_time_to_string (blocked_for,
- GNUNET_YES),
- GST_plugins_a2s (ve->address));
- ve->revalidation_task =
- GNUNET_SCHEDULER_add_delayed (delay,
- &revalidate_address,
- ve);
- ve->next_validation = GNUNET_TIME_relative_to_absolute (delay);
- /* start PINGing by checking blacklist */
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop ("# address revalidations started"), 1,
- GNUNET_NO);
- if (NULL != ve->bc)
- {
- GST_blacklist_test_cancel (ve->bc);
- ve->bc = NULL;
- }
- bc = GST_blacklist_test_allowed (&ve->address->peer,
- ve->address->transport_name,
- &transmit_ping_if_allowed,
- ve,
- NULL,
- NULL);
- if (NULL != bc)
- {
- /* If transmit_ping_if_allowed was already called it may have freed ve,
- * so only set ve->bc if it has not been called.
- */
- ve->bc = bc;
- }
- }
- /**
- * Find a ValidationEntry entry for the given neighbour that matches
- * the given address and transport. If none exists, create one (but
- * without starting any validation).
- *
- * @param address address to find
- * @return validation entry matching the given specifications, NULL
- * if we don't have an existing entry and no public key was given
- */
- static struct ValidationEntry *
- find_validation_entry (const struct GNUNET_HELLO_Address *address)
- {
- struct ValidationEntryMatchContext vemc;
- struct ValidationEntry *ve;
- vemc.ve = NULL;
- vemc.address = address;
- GNUNET_CONTAINER_multipeermap_get_multiple (validation_map,
- &address->peer,
- &validation_entry_match, &vemc);
- if (NULL != (ve = vemc.ve))
- return ve;
- GNUNET_assert (GNUNET_NO ==
- GST_ats_is_known_no_session (address));
- ve = GNUNET_new (struct ValidationEntry);
- ve->in_use = GNUNET_SYSERR; /* not defined */
- ve->address = GNUNET_HELLO_address_copy (address);
- ve->pong_sig_valid_until = GNUNET_TIME_UNIT_ZERO_ABS;
- memset (&ve->pong_sig_cache,
- '\0',
- sizeof(struct GNUNET_CRYPTO_EddsaSignature));
- ve->latency = GNUNET_TIME_UNIT_FOREVER_REL;
- ve->challenge =
- GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX);
- ve->timeout_task =
- GNUNET_SCHEDULER_add_delayed (UNVALIDATED_PING_KEEPALIVE,
- &timeout_hello_validation,
- ve);
- GNUNET_CONTAINER_multipeermap_put (validation_map,
- &address->peer,
- ve,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
- publish_ve_stat_update ();
- validation_entry_changed (ve,
- GNUNET_TRANSPORT_VS_NEW);
- return ve;
- }
- /**
- * Iterator which adds the given address to the set of validated
- * addresses.
- *
- * @param cls original HELLO message
- * @param address the address
- * @param expiration expiration time
- * @return #GNUNET_OK (keep the address), could return
- * #GNUNET_NO (delete address, but this is ignored);
- * #GNUNET_SYSERR would abort iteration (but we always iterate all)
- */
- static int
- add_valid_address (void *cls,
- const struct GNUNET_HELLO_Address *address,
- struct GNUNET_TIME_Absolute expiration)
- {
- const struct GNUNET_HELLO_Message *hello = cls;
- struct ValidationEntry *ve;
- struct GNUNET_PeerIdentity pid;
- struct GNUNET_ATS_Properties prop;
- struct GNUNET_TRANSPORT_PluginFunctions *papi;
- if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
- return GNUNET_OK; /* expired */
- if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid))
- {
- GNUNET_break (0);
- return GNUNET_OK; /* invalid HELLO !? */
- }
- if (NULL == (papi = GST_plugins_find (address->transport_name)))
- {
- /* might have been valid in the past, but we don't have that
- plugin loaded right now */
- return GNUNET_OK;
- }
- if (NULL ==
- papi->address_to_string (papi->cls,
- address->address,
- address->address_length))
- {
- /* Why do we try to add an ill-formed address? */
- GNUNET_break (0);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Address with %u bytes for plugin %s and peer %s is malformed\n",
- (unsigned int) address->address_length,
- address->transport_name,
- GNUNET_i2s (&pid));
- return GNUNET_OK;
- }
- ve = find_validation_entry (address);
- ve->network = papi->get_network_for_address (papi->cls,
- address);
- GNUNET_break (GNUNET_NT_UNSPECIFIED != ve->network);
- ve->valid_until = GNUNET_TIME_absolute_max (ve->valid_until,
- expiration);
- if (NULL == ve->revalidation_task)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Starting revalidations for valid address `%s'\n",
- GST_plugins_a2s (ve->address));
- ve->next_validation = GNUNET_TIME_absolute_get ();
- ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve);
- }
- validation_entry_changed (ve,
- GNUNET_TRANSPORT_VS_UPDATE);
- memset (&prop, 0, sizeof(prop));
- prop.scope = ve->network;
- prop.delay = GNUNET_TIME_relative_divide (ve->latency, 2);
- if (GNUNET_YES != ve->known_to_ats)
- {
- ve->known_to_ats = GNUNET_YES;
- GST_ats_add_address (address, &prop);
- GNUNET_assert (GNUNET_YES ==
- GST_ats_is_known_no_session (ve->address));
- }
- return GNUNET_OK;
- }
- /**
- * Function called for any HELLO known to PEERINFO.
- *
- * @param cls unused (NULL)
- * @param peer id of the peer, NULL for last call (during iteration,
- * as we are monitoring, this should never happen)
- * @param hello hello message for the peer (can be NULL)
- * @param err_msg error message
- */
- static void
- process_peerinfo_hello (void *cls,
- const struct GNUNET_PeerIdentity *peer,
- const struct GNUNET_HELLO_Message *hello,
- const char *err_msg)
- {
- GNUNET_assert (NULL != peer);
- if (NULL == hello)
- return;
- if (0 == memcmp (&GST_my_identity,
- peer,
- sizeof(struct GNUNET_PeerIdentity)))
- {
- /* Peerinfo returned own identity, skip validation */
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Handling HELLO for peer `%s'\n",
- GNUNET_i2s (peer));
- GNUNET_assert (NULL ==
- GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO,
- &add_valid_address,
- (void *) hello));
- }
- /**
- * Start the validation subsystem.
- *
- * @param max_fds maximum number of fds to use
- */
- void
- GST_validation_start (unsigned int max_fds)
- {
- /**
- * Initialization for validation throttling
- *
- * We have a maximum number max_fds of connections we can use for validation
- * We monitor the number of validations in parallel and start to throttle it
- * when doing to many validations in parallel:
- * if (running validations < (max_fds / 2))
- * - "fast start": run validation immediately
- * - have delay of (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value_us) / (max_fds / 2)
- * (300 sec / ~150 == ~2 sec.) between two validations
- */validation_next = GNUNET_TIME_absolute_get ();
- validation_delay.rel_value_us =
- (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value_us) / (max_fds / 2);
- validations_fast_start_threshold = (max_fds / 2);
- validations_running = 0;
- GNUNET_STATISTICS_set (GST_stats,
- gettext_noop ("# validations running"),
- validations_running,
- GNUNET_NO);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Validation uses a fast start threshold of %u connections and a delay of %s\n",
- validations_fast_start_threshold,
- GNUNET_STRINGS_relative_time_to_string (validation_delay,
- GNUNET_YES));
- validation_map = GNUNET_CONTAINER_multipeermap_create (VALIDATION_MAP_SIZE,
- GNUNET_NO);
- pnc = GNUNET_PEERINFO_notify (GST_cfg, GNUNET_YES,
- &process_peerinfo_hello, NULL);
- }
- /**
- * Stop the validation subsystem.
- */
- void
- GST_validation_stop ()
- {
- GNUNET_CONTAINER_multipeermap_iterate (validation_map,
- &cleanup_validation_entry,
- NULL);
- GNUNET_CONTAINER_multipeermap_destroy (validation_map);
- validation_map = NULL;
- GNUNET_PEERINFO_notify_cancel (pnc);
- }
- /**
- * Send the given PONG to the given address.
- *
- * @param cls the PONG message
- * @param valid_until is ZERO if we never validated the address,
- * otherwise a time up to when we consider it (or was) valid
- * @param validation_block is FOREVER if the address is for an unsupported plugin (from PEERINFO)
- * is ZERO if the address is considered valid (no validation needed)
- * otherwise a time in the future if we're currently denying re-validation
- * @param address target address
- */
- static void
- multicast_pong (void *cls,
- struct GNUNET_TIME_Absolute valid_until,
- struct GNUNET_TIME_Absolute validation_block,
- const struct GNUNET_HELLO_Address *address)
- {
- struct TransportPongMessage *pong = cls;
- struct GNUNET_TRANSPORT_PluginFunctions *papi;
- struct GNUNET_ATS_Session *session;
- papi = GST_plugins_find (address->transport_name);
- if (NULL == papi)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Plugin %s not supported, cannot send PONG\n",
- address->transport_name);
- return;
- }
- GNUNET_assert (NULL != papi->send);
- GNUNET_assert (NULL != papi->get_session);
- session = papi->get_session (papi->cls, address);
- if (NULL == session)
- {
- GNUNET_break (0);
- return;
- }
- GST_ats_new_session (address, session);
- papi->send (papi->cls, session,
- (const char *) pong,
- ntohs (pong->header.size),
- PONG_PRIORITY,
- ACCEPTABLE_PING_DELAY,
- NULL, NULL);
- GST_neighbours_notify_data_sent (address,
- session,
- pong->header.size);
- }
- /**
- * We've received a PING. If appropriate, generate a PONG.
- *
- * @param sender peer sending the PING
- * @param hdr the PING
- * @param sender_address the sender address as we got it
- * @param session session we got the PING from
- * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
- */
- int
- GST_validation_handle_ping (const struct GNUNET_PeerIdentity *sender,
- const struct GNUNET_MessageHeader *hdr,
- const struct GNUNET_HELLO_Address *sender_address,
- struct GNUNET_ATS_Session *session)
- {
- const struct TransportPingMessage *ping;
- struct TransportPongMessage *pong;
- struct GNUNET_TRANSPORT_PluginFunctions *papi;
- struct GNUNET_CRYPTO_EddsaSignature *sig_cache;
- struct GNUNET_TIME_Absolute *sig_cache_exp;
- const char *addr;
- const char *addrend;
- char *plugin_name;
- char *pos;
- size_t len_address;
- size_t len_plugin;
- ssize_t ret;
- struct GNUNET_HELLO_Address address;
- if (0 ==
- memcmp (&GST_my_identity,
- sender,
- sizeof(struct GNUNET_PeerIdentity)))
- return GNUNET_OK; /* our own, ignore! */
- if (ntohs (hdr->size) < sizeof(struct TransportPingMessage))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- ping = (const struct TransportPingMessage *) hdr;
- if (0 !=
- memcmp (&ping->target,
- &GST_my_identity,
- sizeof(struct GNUNET_PeerIdentity)))
- {
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop
- ("# PING message for different peer received"),
- 1,
- GNUNET_NO);
- return GNUNET_SYSERR;
- }
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop ("# PING messages received"), 1,
- GNUNET_NO);
- addr = (const char *) &ping[1];
- len_address = ntohs (hdr->size) - sizeof(struct TransportPingMessage);
- /* peer wants to confirm that this is one of our addresses, this is what is
- * used for address validation */
- sig_cache = NULL;
- sig_cache_exp = NULL;
- papi = NULL;
- if (len_address > 0)
- {
- addrend = memchr (addr, '\0', len_address);
- if (NULL == addrend)
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- addrend++;
- len_plugin = strlen (addr) + 1;
- len_address -= len_plugin;
- address.local_info = GNUNET_HELLO_ADDRESS_INFO_NONE;
- address.address = addrend;
- address.address_length = len_address;
- address.transport_name = addr;
- address.peer = GST_my_identity;
- if (NULL == address.transport_name)
- {
- GNUNET_break (0);
- }
- if (0 != strstr (address.transport_name, "_client"))
- {
- plugin_name = GNUNET_strdup (address.transport_name);
- pos = strstr (plugin_name, "_client");
- GNUNET_assert (NULL != pos);
- GNUNET_snprintf (pos, strlen ("_server") + 1, "%s", "_server");
- }
- else
- plugin_name = GNUNET_strdup (address.transport_name);
- if (NULL == (papi = GST_plugins_find (plugin_name)))
- {
- /* we don't have the plugin for this address */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _ (
- "Plugin `%s' not available, cannot confirm having this address\n"),
- plugin_name);
- GNUNET_free (plugin_name);
- return GNUNET_SYSERR;
- }
- GNUNET_free (plugin_name);
- if (GNUNET_OK !=
- papi->check_address (papi->cls,
- addrend,
- len_address))
- {
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop
- ("# failed address checks during validation"),
- 1,
- GNUNET_NO);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _ (
- "Address `%s' is not one of my addresses, not confirming PING\n"),
- GST_plugins_a2s (&address));
- return GNUNET_SYSERR;
- }
- else
- {
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop
- (
- "# successful address checks during validation"),
- 1,
- GNUNET_NO);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Address `%s' is one of my addresses, confirming PING\n",
- GST_plugins_a2s (&address));
- }
- if (GNUNET_YES !=
- GST_hello_test_address (&address,
- &sig_cache,
- &sig_cache_exp))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _ (
- "Not confirming PING from peer `%s' with address `%s' since I cannot confirm having this address.\n"),
- GNUNET_i2s (sender),
- GST_plugins_a2s (&address));
- return GNUNET_SYSERR;
- }
- }
- else
- {
- addrend = NULL; /* make gcc happy */
- len_plugin = 0;
- static struct GNUNET_CRYPTO_EddsaSignature no_address_signature;
- static struct GNUNET_TIME_Absolute no_address_signature_expiration;
- sig_cache = &no_address_signature;
- sig_cache_exp = &no_address_signature_expiration;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "I am `%s', sending PONG to peer `%s'\n",
- GNUNET_i2s_full (&GST_my_identity),
- GNUNET_i2s (sender));
- /* message with structure:
- * [TransportPongMessage][Transport name][Address] */
- pong = GNUNET_malloc (sizeof(struct TransportPongMessage) + len_address
- + len_plugin);
- pong->header.size =
- htons (sizeof(struct TransportPongMessage) + len_address + len_plugin);
- pong->header.type = htons (GNUNET_MESSAGE_TYPE_TRANSPORT_PONG);
- pong->purpose.size =
- htonl (sizeof(struct GNUNET_CRYPTO_EccSignaturePurpose)
- + sizeof(uint32_t) + sizeof(struct GNUNET_TIME_AbsoluteNBO)
- + len_address + len_plugin);
- pong->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN);
- GNUNET_memcpy (&pong->challenge, &ping->challenge, sizeof(ping->challenge));
- pong->addrlen = htonl (len_address + len_plugin);
- GNUNET_memcpy (&pong[1], addr, len_plugin); /* Copy transport plugin */
- if (len_address > 0)
- {
- GNUNET_assert (NULL != addrend);
- GNUNET_memcpy (&((char *) &pong[1])[len_plugin], addrend, len_address);
- }
- if (GNUNET_TIME_absolute_get_remaining (*sig_cache_exp).rel_value_us <
- PONG_SIGNATURE_LIFETIME.rel_value_us / 4)
- {
- /* create / update cached sig */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Creating PONG signature to indicate ownership.\n");
- *sig_cache_exp = GNUNET_TIME_relative_to_absolute (PONG_SIGNATURE_LIFETIME);
- pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp);
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_sign_ (&GST_my_private_key,
- &pong->purpose,
- sig_cache))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _ ("Failed to create PONG signature for peer `%s'\n"),
- GNUNET_i2s (sender));
- }
- }
- else
- {
- pong->expiration = GNUNET_TIME_absolute_hton (*sig_cache_exp);
- }
- pong->signature = *sig_cache;
- GNUNET_assert (NULL != sender_address);
- /* first see if the session we got this PING from can be used to transmit
- * a response reliably */
- if (NULL == papi)
- {
- ret = -1;
- }
- else
- {
- GNUNET_assert (NULL != papi->send);
- GNUNET_assert (NULL != papi->get_session);
- if (NULL == session)
- {
- session = papi->get_session (papi->cls, sender_address);
- }
- if (NULL == session)
- {
- GNUNET_break (0);
- ret = -1;
- }
- else
- {
- ret = papi->send (papi->cls, session,
- (const char *) pong,
- ntohs (pong->header.size),
- PONG_PRIORITY, ACCEPTABLE_PING_DELAY,
- NULL, NULL);
- if (-1 != ret)
- GST_neighbours_notify_data_sent (sender_address,
- session,
- pong->header.size);
- }
- }
- if (-1 != ret)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Transmitted PONG to `%s' via reliable mechanism\n",
- GNUNET_i2s (sender));
- /* done! */
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop
- ("# PONGs unicast via reliable transport"), 1,
- GNUNET_NO);
- GNUNET_free (pong);
- return GNUNET_OK;
- }
- /* no reliable method found, try transmission via all known addresses */
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop
- ("# PONGs multicast to all available addresses"),
- 1,
- GNUNET_NO);
- GST_validation_get_addresses (sender,
- &multicast_pong, pong);
- GNUNET_free (pong);
- return GNUNET_OK;
- }
- /**
- * Validate an individual address.
- *
- * @param address address we should try to validate
- */
- void
- GST_validation_handle_address (const struct GNUNET_HELLO_Address *address)
- {
- struct GNUNET_TRANSPORT_PluginFunctions *papi;
- struct ValidationEntry *ve;
- papi = GST_plugins_find (address->transport_name);
- if (NULL == papi)
- {
- /* This plugin is currently unvailable ... ignore */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "No plugin available for %s\n",
- address->transport_name);
- return;
- }
- ve = find_validation_entry (address);
- if (NULL == ve->revalidation_task)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Validation process started for fresh address `%s' of %s\n",
- GST_plugins_a2s (ve->address),
- GNUNET_i2s (&ve->address->peer));
- ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address, ve);
- }
- else
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Validation already running for address `%s' of %s\n",
- GST_plugins_a2s (ve->address),
- GNUNET_i2s (&ve->address->peer));
- }
- }
- /**
- * Iterator callback to go over all addresses and try to validate them
- * (unless blocked or already validated).
- *
- * @param cls NULL
- * @param address the address
- * @param expiration expiration time
- * @return #GNUNET_OK (keep the address)
- */
- static int
- validate_address_iterator (void *cls,
- const struct GNUNET_HELLO_Address *address,
- struct GNUNET_TIME_Absolute expiration)
- {
- if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Skipping expired address from HELLO\n");
- return GNUNET_OK; /* expired */
- }
- GST_validation_handle_address (address);
- return GNUNET_OK;
- }
- /**
- * Add the validated peer address to the HELLO.
- *
- * @param cls the `struct ValidationEntry *` with the validated address
- * @param max space in @a buf
- * @param buf where to add the address
- * @return number of bytes written, #GNUNET_SYSERR to signal the
- * end of the iteration.
- */
- static ssize_t
- add_valid_peer_address (void *cls,
- size_t max,
- void *buf)
- {
- struct ValidationEntry *ve = cls;
- if (GNUNET_YES == ve->copied)
- return GNUNET_SYSERR; /* Done */
- ve->copied = GNUNET_YES;
- return GNUNET_HELLO_add_address (ve->address,
- ve->valid_until,
- buf,
- max);
- }
- /**
- * We've received a PONG. Check if it matches a pending PING and
- * mark the respective address as confirmed.
- *
- * @param sender peer sending the PONG
- * @param hdr the PONG
- * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
- */
- int
- GST_validation_handle_pong (const struct GNUNET_PeerIdentity *sender,
- const struct GNUNET_MessageHeader *hdr)
- {
- const struct TransportPongMessage *pong;
- struct ValidationEntry *ve;
- const char *tname;
- const char *addr;
- size_t addrlen;
- size_t slen;
- size_t size;
- struct GNUNET_HELLO_Message *hello;
- struct GNUNET_HELLO_Address address;
- int sig_res;
- int do_verify;
- if (0 ==
- memcmp (&GST_my_identity,
- sender,
- sizeof(struct GNUNET_PeerIdentity)))
- return GNUNET_OK; /* our own, ignore! */
- if (ntohs (hdr->size) < sizeof(struct TransportPongMessage))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop ("# PONG messages received"), 1,
- GNUNET_NO);
- /* message with structure:
- * [TransportPongMessage][Transport name][Address] */
- pong = (const struct TransportPongMessage *) hdr;
- tname = (const char *) &pong[1];
- size = ntohs (hdr->size) - sizeof(struct TransportPongMessage);
- addr = memchr (tname, '\0', size);
- if (NULL == addr)
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- addr++;
- slen = strlen (tname) + 1;
- addrlen = size - slen;
- if (NULL == GST_plugins_find (tname))
- {
- /* we got the PONG, but the transport plugin specified in it
- is not supported by this peer, so this cannot be a good
- PONG for us. */
- GNUNET_break_op (0);
- return GNUNET_OK;
- }
- address.peer = *sender;
- address.address = addr;
- address.address_length = addrlen;
- address.transport_name = tname;
- address.local_info = GNUNET_HELLO_ADDRESS_INFO_NONE;
- ve = find_validation_entry (&address);
- if ((NULL == ve) || (GNUNET_NO == ve->expecting_pong))
- {
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop
- (
- "# PONGs dropped, no matching pending validation"),
- 1, GNUNET_NO);
- return GNUNET_OK;
- }
- /* now check that PONG is well-formed */
- if (0 != memcmp (&ve->address->peer,
- sender,
- sizeof(struct GNUNET_PeerIdentity)))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- if (0 ==
- GNUNET_TIME_absolute_get_remaining
- (GNUNET_TIME_absolute_ntoh (pong->expiration)).rel_value_us)
- {
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop
- ("# PONGs dropped, signature expired"), 1,
- GNUNET_NO);
- return GNUNET_SYSERR;
- }
- sig_res = GNUNET_SYSERR;
- do_verify = GNUNET_YES;
- if (0 != GNUNET_TIME_absolute_get_remaining (
- ve->pong_sig_valid_until).rel_value_us)
- {
- /* We have a cached and valid signature for this peer,
- * try to compare instead of verify */
- if (0 == memcmp (&ve->pong_sig_cache,
- &pong->signature,
- sizeof(struct GNUNET_CRYPTO_EddsaSignature)))
- {
- /* signatures are identical, we can skip verification */
- sig_res = GNUNET_OK;
- do_verify = GNUNET_NO;
- }
- else
- {
- sig_res = GNUNET_SYSERR;
- /* signatures do not match, we have to verify */
- }
- }
- if (GNUNET_YES == do_verify)
- {
- /* Do expensive verification */
- sig_res = GNUNET_CRYPTO_eddsa_verify_ (
- GNUNET_SIGNATURE_PURPOSE_TRANSPORT_PONG_OWN,
- &pong->purpose,
- &pong->signature,
- &ve->address->peer.public_key);
- if (sig_res == GNUNET_SYSERR)
- {
- GNUNET_break_op (0);
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Failed to verify: invalid signature on address `%s':%s from peer `%s'\n",
- tname,
- GST_plugins_a2s (ve->address),
- GNUNET_i2s (sender));
- }
- }
- if (sig_res == GNUNET_SYSERR)
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Validation process successful for peer `%s' with plugin `%s' address `%s'\n",
- GNUNET_i2s (sender),
- tname,
- GST_plugins_a2s (ve->address));
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop ("# validations succeeded"),
- 1,
- GNUNET_NO);
- /* validity achieved, remember it! */
- ve->expecting_pong = GNUNET_NO;
- ve->valid_until = GNUNET_TIME_relative_to_absolute (HELLO_ADDRESS_EXPIRATION);
- ve->pong_sig_cache = pong->signature;
- ve->pong_sig_valid_until = GNUNET_TIME_absolute_ntoh (pong->expiration);
- ve->latency = GNUNET_TIME_absolute_get_duration (ve->send_time);
- {
- if (GNUNET_YES == ve->known_to_ats)
- {
- GNUNET_assert (GNUNET_YES ==
- GST_ats_is_known_no_session (ve->address));
- GST_ats_update_delay (ve->address,
- GNUNET_TIME_relative_divide (ve->latency, 2));
- }
- else
- {
- struct GNUNET_ATS_Properties prop;
- memset (&prop, 0, sizeof(prop));
- GNUNET_break (GNUNET_NT_UNSPECIFIED != ve->network);
- prop.scope = ve->network;
- prop.delay = GNUNET_TIME_relative_divide (ve->latency, 2);
- GNUNET_assert (GNUNET_NO ==
- GST_ats_is_known_no_session (ve->address));
- ve->known_to_ats = GNUNET_YES;
- GST_ats_add_address (ve->address, &prop);
- GNUNET_assert (GNUNET_YES ==
- GST_ats_is_known_no_session (ve->address));
- }
- }
- if (validations_running > 0)
- {
- validations_running--;
- GNUNET_STATISTICS_set (GST_stats,
- gettext_noop ("# validations running"),
- validations_running,
- GNUNET_NO);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Validation finished, %u validation processes running\n",
- validations_running);
- }
- else
- {
- GNUNET_break (0);
- }
- /* Notify about new validity */
- validation_entry_changed (ve,
- GNUNET_TRANSPORT_VS_UPDATE);
- /* build HELLO to store in PEERINFO */
- GNUNET_STATISTICS_update (GST_stats,
- gettext_noop ("# HELLOs given to peerinfo"),
- 1,
- GNUNET_NO);
- ve->copied = GNUNET_NO;
- hello = GNUNET_HELLO_create (&ve->address->peer.public_key,
- &add_valid_peer_address,
- ve,
- GNUNET_NO);
- GNUNET_break (NULL !=
- GNUNET_PEERINFO_add_peer (GST_peerinfo,
- hello,
- NULL,
- NULL));
- GNUNET_free (hello);
- return GNUNET_OK;
- }
- /**
- * We've received a HELLO, check which addresses are new and trigger
- * validation.
- *
- * @param hello the HELLO we received
- * @return #GNUNET_OK if the message was fine, #GNUNET_SYSERR on serious error
- */
- int
- GST_validation_handle_hello (const struct GNUNET_MessageHeader *hello)
- {
- const struct GNUNET_HELLO_Message *hm =
- (const struct GNUNET_HELLO_Message *) hello;
- struct GNUNET_PeerIdentity pid;
- int friend;
- friend = GNUNET_HELLO_is_friend_only (hm);
- if (((GNUNET_YES != friend) &&
- (GNUNET_NO != friend)) ||
- (GNUNET_OK != GNUNET_HELLO_get_id (hm, &pid)))
- {
- /* malformed HELLO */
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- if (0 ==
- memcmp (&GST_my_identity,
- &pid,
- sizeof(struct GNUNET_PeerIdentity)))
- {
- /* got our own HELLO, how boring */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Validation received our own HELLO (%s), ignoring\n",
- GNUNET_i2s (&pid));
- return GNUNET_OK;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Validation received HELLO message for peer `%s' with size %u, checking for new addresses\n",
- GNUNET_i2s (&pid),
- ntohs (hello->size));
- GNUNET_assert (NULL ==
- GNUNET_HELLO_iterate_addresses (hm,
- GNUNET_NO,
- &validate_address_iterator,
- NULL));
- return GNUNET_OK;
- }
- /**
- * Closure for #iterate_addresses().
- */
- struct IteratorContext
- {
- /**
- * Function to call on each address.
- */
- GST_ValidationAddressCallback cb;
- /**
- * Closure for @e cb.
- */
- void *cb_cls;
- };
- /**
- * Call the callback in the closure for each validation entry.
- *
- * @param cls the `struct IteratorContext`
- * @param key the peer's identity
- * @param value the `struct ValidationEntry`
- * @return #GNUNET_OK (continue to iterate)
- */
- static int
- iterate_addresses (void *cls,
- const struct GNUNET_PeerIdentity *key,
- void *value)
- {
- struct IteratorContext *ic = cls;
- struct ValidationEntry *ve = value;
- ic->cb (ic->cb_cls,
- ve->valid_until,
- ve->revalidation_block,
- ve->address);
- return GNUNET_OK;
- }
- /**
- * Call the given function for each address for the given target.
- * Can either give a snapshot (synchronous API) or be continuous.
- *
- * @param target peer information is requested for
- * @param cb function to call; will not be called after this function returns
- * @param cb_cls closure for @a cb
- */
- void
- GST_validation_get_addresses (const struct GNUNET_PeerIdentity *target,
- GST_ValidationAddressCallback cb,
- void *cb_cls)
- {
- struct IteratorContext ic;
- ic.cb = cb;
- ic.cb_cls = cb_cls;
- GNUNET_CONTAINER_multipeermap_get_multiple (validation_map,
- target,
- &iterate_addresses, &ic);
- }
- /**
- * Update if we are using an address for a connection actively right now.
- * Based on this, the validation module will measure latency for the
- * address more or less often.
- *
- * @param address the address that we are now using (or not)
- * @param in_use #GNUNET_YES if we are now using the address for a connection,
- * #GNUNET_NO if we are no longer using the address for a connection
- */
- void
- GST_validation_set_address_use (const struct GNUNET_HELLO_Address *address,
- int in_use)
- {
- struct ValidationEntry *ve;
- if (GNUNET_HELLO_address_check_option (address,
- GNUNET_HELLO_ADDRESS_INFO_INBOUND))
- return; /* ignore inbound for validation */
- if (NULL == GST_plugins_find (address->transport_name))
- {
- /* How can we use an address for which we don't have the plugin? */
- GNUNET_break (0);
- return;
- }
- ve = find_validation_entry (address);
- if (NULL == ve)
- {
- GNUNET_break (0);
- return;
- }
- if (in_use == ve->in_use)
- return;
- ve->in_use = in_use;
- if (GNUNET_YES == in_use)
- {
- /* from now on, higher frequeny, so reschedule now */
- if (NULL != ve->revalidation_task)
- GNUNET_SCHEDULER_cancel (ve->revalidation_task);
- ve->revalidation_task = GNUNET_SCHEDULER_add_now (&revalidate_address,
- ve);
- }
- }
- /* end of file gnunet-service-transport_validation.c */
|