/** * Record activity on the node. */ void stable_record_activity(const knode_t *kn) { struct lifedata *ld; struct lifedata new_ld; knode_check(kn); g_assert(kn->flags & KNODE_F_ALIVE); ld = get_lifedata(kn->id); if (NULL == ld) { ld = &new_ld; new_ld.version = LIFEDATA_STRUCT_VERSION; new_ld.first_seen = kn->first_seen; new_ld.last_seen = kn->last_seen; gnet_stats_count_general(GNR_DHT_STABLE_NODES_HELD, +1); } else { if (kn->last_seen <= ld->last_seen) return; ld->last_seen = kn->last_seen; } dbmw_write(db_lifedata, kn->id->v, ld, sizeof *ld); }
/** * Notification from the socket layer that we got a new datagram. * * @param s the receiving socket (with s->addr and s->port set) * @param data start of received data (not necessarily s->buf) * @param len length of received data (not necessarily s->pos) * @param truncated whether received datagram was truncated * * If `truncated' is true, then the message was too large for the * socket buffer. */ void udp_received(const gnutella_socket_t *s, const void *data, size_t len, bool truncated) { gnutella_node_t *n; bool bogus = FALSE; bool dht = FALSE; bool rudp = FALSE; /* * This must be regular Gnutella / DHT traffic. */ inet_udp_got_incoming(s->addr); /* * We need to identify semi-reliable UDP traffic early, because that * traffic needs to go through the RX stack to reassemble the final * payload out of the many fragments, or to process the acknowledgments. * * We have to apply heuristics however because the leading 8 bytes could * be just a part of gnutella message (the first 8 bytes of a GUID). * One thing is certain though: if the size is less than that of a a * Gnutella header, it has to be semi-reliable UDP traffic... * * Because semi-reliable UDP uses small payloads, much smaller than our * socket buffer, the datagram cannot be truncated. */ if (!truncated) { enum udp_traffic utp; rxdrv_t *rx; utp = udp_intuit_traffic_type(s, data, len); switch (utp) { case GNUTELLA: goto unreliable; case RUDP: rudp = TRUE; gnet_stats_count_general(GNR_RUDP_RX_BYTES, len); goto rudp; /* Don't account this message in UDP statistics */ case DHT: dht = TRUE; goto unreliable; case UNKNOWN: goto unknown; case SEMI_RELIABLE_GTA: case SEMI_RELIABLE_GND: break; } /* * We are going to treat this message a a semi-reliable UDP fragment. * * Account the size of the payload for traffic purposes, then redirect * the message to the RX layer that reassembles and dispatches these * messages. */ bws_udp_count_read(len, FALSE); /* We know it's not DHT traffic */ rx = udp_get_rx_semi_reliable(utp, s->addr, len); if (rx != NULL) { gnet_host_t from; gnet_host_set(&from, s->addr, s->port); ut_got_message(rx, data, len, &from); } return; } unknown: /* * Discriminate between Gnutella UDP and DHT messages, so that we * can account received data with the proper bandwidth scheduler. */ if (len >= GTA_HEADER_SIZE) dht = GTA_MSG_DHT == gnutella_header_get_function(data); /* FALL THROUGH */ unreliable: /* * Account for Gnutella / DHT incoming UDP traffic. */ bws_udp_count_read(len, dht); /* FALL THROUGH */ rudp: /* * The RUDP layer is used to implement firewalled-to-firewalled transfers * via a mini TCP-like layer built on top of UDP. Therefore, it is used * as the basis for higher-level connections (HTTP) and will have to be * accounted for once the type of traffic is known, by upper layers, as * part of the upload/download traffic. * * Of course, the higher levels will never see all the bytes that pass * through, such as acknowledgments or retransmissions, but that is also * the case for TCP-based sockets. * --RAM, 2012-11-02. */ /* * If we get traffic from a bogus IP (unroutable), warn, for now. */ if (bogons_check(s->addr)) { bogus = TRUE; if (GNET_PROPERTY(udp_debug)) { g_warning("UDP %sdatagram (%zu byte%s) received from bogus IP %s", truncated ? "truncated " : "", len, 1 == len ? "" : "s", host_addr_to_string(s->addr)); } gnet_stats_inc_general(GNR_UDP_BOGUS_SOURCE_IP); } /* * Get proper pseudo-node. * * These routines can return NULL if the address/port combination is * not correct, but this will be handled by udp_is_valid_gnet(). */ n = dht ? node_dht_get_addr_port(s->addr, s->port) : node_udp_get_addr_port(s->addr, s->port); if (!udp_is_valid_gnet(n, s, truncated, data, len)) return; /* * RUDP traffic does not go to the upper Gnutella processing layers. */ if (rudp) { /* Not ready for prime time */ #if 0 rudp_handle_packet(s->addr, s->port. data, len); #endif return; } /* * Process message as if it had been received from regular Gnet by * another node, only we'll use a special "pseudo UDP node" as origin. */ if (GNET_PROPERTY(udp_debug) > 19 || (bogus && GNET_PROPERTY(udp_debug))) g_debug("UDP got %s from %s%s", gmsg_infostr_full(data, len), bogus ? "BOGUS " : "", host_addr_port_to_string(s->addr, s->port)); node_udp_process(n, s, data, len); }
/** * Fill supplied value vector with the DHT values we have under the key that * match the specifications: among those bearing the specified secondary keys * (or all of them if no secondary keys are supplied), return only those with * the proper DHT value type. * * @param id the primary key of the value * @param type type of DHT value they want * @param secondary optional secondary keys * @param secondary_count amount of secondary keys supplied * @param valvec value vector where results are stored * @param valcnt size of value vector * @param loadptr where to write the average request load for key * @param cached if non-NULL, filled with whether key was cached * * @return amount of values filled into valvec. The values are dynamically * created and must be freed by caller through dht_value_free(). */ int keys_get(const kuid_t *id, dht_value_type_t type, kuid_t **secondary, int secondary_count, dht_value_t **valvec, int valcnt, float *loadptr, bool *cached) { struct keyinfo *ki; struct keydata *kd; int i; int vcnt = valcnt; dht_value_t **vvec = valvec; g_assert(secondary_count == 0 || secondary != NULL); g_assert(valvec); g_assert(valcnt > 0); g_assert(loadptr); ki = hikset_lookup(keys, id); g_assert(ki); /* If called, we know the key exists */ if (GNET_PROPERTY(dht_storage_debug) > 5) g_debug("DHT FETCH key %s (load = %g, current reqs = %u) type %s" " with %d secondary key%s", kuid_to_hex_string(id), ki->get_req_load, ki->get_requests, dht_value_type_to_string(type), secondary_count, plural(secondary_count)); *loadptr = ki->get_req_load; ki->get_requests++; kd = get_keydata(id); if (kd == NULL) /* DB failure */ return 0; /* * If secondary keys were requested, lookup them up and make sure * they have the right DHT type (or skip them). */ for (i = 0; i < secondary_count && vcnt > 0; i++) { uint64 dbkey = lookup_secondary(kd, secondary[i]); dht_value_t *v; if (0 == dbkey) continue; v = values_get(dbkey, type); if (v == NULL) continue; g_assert(kuid_eq(dht_value_key(v), id)); if (GNET_PROPERTY(dht_storage_debug) > 5) g_debug("DHT FETCH key %s via secondary key %s has matching %s", kuid_to_hex_string(id), kuid_to_hex_string2(secondary[i]), dht_value_to_string(v)); *vvec++ = v; vcnt--; } /* * Don't count secondary-key fetches in the local hit stats: in order to * be able to get these fetches, we must have initially provided the * list of these keys, and thus we have already traversed the code below * for that fetch, which accounted the hit already. */ if (secondary_count) { int n = vvec - valvec; /* Amount of entries filled */ gnet_stats_count_general(GNR_DHT_CLAIMED_SECONDARY_KEYS, n); if (ki->flags & DHT_KEY_F_CACHED) gnet_stats_count_general(GNR_DHT_CLAIMED_CACHED_SECONDARY_KEYS, n); goto done; } /* * No secondary keys specified. Look them all up. */ for (i = 0; i < kd->values && vcnt > 0; i++) { uint64 dbkey = kd->dbkeys[i]; dht_value_t *v; g_assert(0 != dbkey); v = values_get(dbkey, type); if (v == NULL) continue; g_assert(kuid_eq(dht_value_key(v), id)); if (GNET_PROPERTY(dht_storage_debug) > 5) g_debug("DHT FETCH key %s has matching %s", kuid_to_hex_string(id), dht_value_to_string(v)); *vvec++ = v; vcnt--; } /* * Stats update: we count all the hits, plus successful hits on keys * that do not fall within our k-ball, i.e. keys for which we act as * a "cache". Note that our k-ball frontier can evolve through time, * so we rely on the DHT_KEY_F_CACHED flag, positionned at creation time. */ if (vvec != valvec) { gnet_stats_inc_general(GNR_DHT_FETCH_LOCAL_HITS); if (ki->flags & DHT_KEY_F_CACHED) gnet_stats_inc_general(GNR_DHT_FETCH_LOCAL_CACHED_HITS); } done: if (cached) *cached = (ki->flags & DHT_KEY_F_CACHED) ? TRUE : FALSE; return vvec - valvec; /* Amount of entries filled */ }