void gnet_stats_count_flowc(const void *head, bool head_only) { uint t; uint16 size = gmsg_size(head) + GTA_HEADER_SIZE; uint8 function = gnutella_header_get_function(head); uint8 ttl = gnutella_header_get_ttl(head); uint8 hops = gnutella_header_get_hops(head); g_assert(thread_is_main()); if (GNET_PROPERTY(node_debug) > 3) g_debug("FLOWC function=%d ttl=%d hops=%d", function, ttl, hops); /* * Adjust for Kademlia messages. */ if (GTA_MSG_DHT == function && size >= KDA_HEADER_SIZE && !head_only) { uint8 opcode = kademlia_header_get_function(head); if (UNSIGNED(opcode + MSG_DHT_BASE) < G_N_ELEMENTS(stats_lut)) { t = stats_lut[opcode + MSG_DHT_BASE]; } else { t = stats_lut[function]; /* Invalid opcode? */ } hops = 0; ttl = 0; } else { t = stats_lut[function]; } gnet_stats_flowc_internal(t, function, ttl, hops, size); }
void gnet_stats_count_sent(const gnutella_node_t *n, uint8 type, const void *base, uint32 size) { uint t = stats_lut[type]; gnet_stats_t *stats; uint8 hops; g_assert(t != MSG_UNKNOWN); g_assert(thread_is_main()); g_assert(!NODE_TALKS_G2(n)); stats = NODE_USES_UDP(n) ? &gnet_udp_stats : &gnet_tcp_stats; /* * Adjust for Kademlia messages. */ if (GTA_MSG_DHT == type && size >= KDA_HEADER_SIZE) { uint8 opcode = kademlia_header_get_function(base); if (UNSIGNED(opcode + MSG_DHT_BASE) < G_N_ELEMENTS(stats_lut)) { t = stats_lut[opcode + MSG_DHT_BASE]; } hops = 0; } else { hops = gnutella_header_get_hops(base); } gnet_stats_count_sent_internal(n, t, hops, size, stats); }
/** * Called when Gnutella header has been read. */ void gnet_stats_count_received_header(gnutella_node_t *n) { uint t = stats_lut[gnutella_header_get_function(&n->header)]; uint8 ttl, hops; g_assert(thread_is_main()); g_assert(!NODE_TALKS_G2(n)); ttl = gnutella_header_get_ttl(&n->header); hops = gnutella_header_get_hops(&n->header); gnet_stats_count_received_header_internal(n, GTA_HEADER_SIZE, t, ttl, hops); }
/** * Called to transform Gnutella header counting into Kademlia header counting. * * @param n the node receiving the message * @param kt */ static void gnet_stats_count_kademlia_header(const gnutella_node_t *n, uint kt) { uint t = stats_lut[gnutella_header_get_function(&n->header)]; uint i; gnet_stats_t *stats; g_assert(thread_is_main()); g_assert(!NODE_TALKS_G2(n)); stats = NODE_USES_UDP(n) ? &gnet_udp_stats : &gnet_tcp_stats; gnet_stats.pkg.received[t]--; gnet_stats.pkg.received[kt]++; gnet_stats.byte.received[t] -= GTA_HEADER_SIZE; gnet_stats.byte.received[kt] += GTA_HEADER_SIZE; stats->pkg.received[t]--; stats->pkg.received[kt]++; stats->byte.received[t] -= GTA_HEADER_SIZE; stats->byte.received[kt] += GTA_HEADER_SIZE; i = MIN(gnutella_header_get_ttl(&n->header), STATS_RECV_COLUMNS - 1); stats->pkg.received_ttl[i][MSG_TOTAL]--; stats->pkg.received_ttl[i][t]--; i = MIN(gnutella_header_get_hops(&n->header), STATS_RECV_COLUMNS - 1); stats->pkg.received_hops[i][MSG_TOTAL]--; stats->pkg.received_hops[i][t]--; /* DHT messages have no hops nor ttl, use 0 */ stats->pkg.received_ttl[0][MSG_TOTAL]++; stats->pkg.received_ttl[0][kt]++; stats->pkg.received_hops[0][MSG_TOTAL]++; stats->pkg.received_hops[0][kt]++; }
/** * Called when Gnutella payload has been read, or when a G2 messsage is read. * * The actual payload size (effectively read) is expected to be found * in n->size for Gnutella messages and G2 messages. * * @param n the node from which message was received * @param payload start of Gnutella payload, or head of G2 frame */ void gnet_stats_count_received_payload(const gnutella_node_t *n, const void *payload) { uint8 f; uint t; uint i; gnet_stats_t *stats; uint32 size; uint8 hops, ttl; g_assert(thread_is_main()); stats = NODE_USES_UDP(n) ? &gnet_udp_stats : &gnet_tcp_stats; size = n->size; /* * Size is NOT read in the Gnutella header but in n->size, which * reflects how much data we have in the payload, as opposed to the * size in the header which may be wrong, or have highest bits set * because they indicate flags. * * In particular, broken DHT messages often come with an invalid size in * the header. * --RAM, 2010-10-30 */ if (NODE_TALKS_G2(n)) { f = g2_msg_type(payload, size); if (f != G2_MSG_MAX) { f += MSG_G2_BASE; } else { f = G_N_ELEMENTS(stats_lut) - 1; /* Last, holds MSG_UNKNOWN */ } ttl = 1; hops = NODE_USES_UDP(n) ? 0 : 1; t = stats_lut[f]; /* * No header for G2, so count header reception now with a size of zero. * This is required to update the other packet reception statistics. */ gnet_stats_count_received_header_internal( deconstify_pointer(n), 0, t, ttl, hops); } else { f = gnutella_header_get_function(&n->header); hops = gnutella_header_get_hops(&n->header); ttl = gnutella_header_get_ttl(&n->header); t = stats_lut[f]; } gnet_stats_randomness(n, f, size); /* * If we're dealing with a Kademlia message, we need to do two things: * * We counted the Gnutella header for the GTA_MSG_DHT message, but we * now need to undo that and count it as a Kademlia message. * * To access the proper entry in the array, we need to offset the * Kademlia OpCode from the header with MSG_DHT_BASE to get the entry * in the statistics that are associated with that particular message. * --RAM, 2010-11-01 */ if (GTA_MSG_DHT == f && size + GTA_HEADER_SIZE >= KDA_HEADER_SIZE) { uint8 opcode = peek_u8(payload); /* Kademlia Opcode */ if (UNSIGNED(opcode + MSG_DHT_BASE) < G_N_ELEMENTS(stats_lut)) { t = stats_lut[opcode + MSG_DHT_BASE]; gnet_stats_count_kademlia_header(n, t); } } g_assert(t < MSG_TOTAL); gnet_stats.byte.received[MSG_TOTAL] += size; gnet_stats.byte.received[t] += size; stats->byte.received[MSG_TOTAL] += size; stats->byte.received[t] += size; i = MIN(ttl, STATS_RECV_COLUMNS - 1); stats->byte.received_ttl[i][MSG_TOTAL] += size; stats->byte.received_ttl[i][t] += size; i = MIN(hops, STATS_RECV_COLUMNS - 1); stats->byte.received_hops[i][MSG_TOTAL] += size; stats->byte.received_hops[i][t] += size; }
/** * Identify the traffic type received on the UDP socket. * * This routine uses simple heuristics that ensure we're properly discriminating * incoming traffic on the UDP socket between regular Gnutella traffic and * semi-reliable UDP traffic (which adds a small header before its actual * payload). * * Most messages will be un-ambiguous, and the probabilty of misclassifying * an ambiguous message (one that look like valid for both types, based on * header inspections) is brought down to less than 1 in a billion, making * it perfectly safe in practice. * * @return intuited type */ static enum udp_traffic udp_intuit_traffic_type(const gnutella_socket_t *s, const void *data, size_t len) { enum udp_traffic utp; utp = udp_check_semi_reliable(data, len); if (len >= GTA_HEADER_SIZE) { uint16 size; /* Payload size, from the Gnutella message */ gmsg_valid_t valid; valid = gmsg_size_valid(data, &size); switch (valid) { case GMSG_VALID: case GMSG_VALID_MARKED: if ((size_t) size + GTA_HEADER_SIZE == len) { uint8 function, hops, ttl; function = gnutella_header_get_function(data); /* * If the header cannot be that of a known semi-reliable * UDP protocol, there is no ambiguity. */ if (UNKNOWN == utp) { return GTA_MSG_DHT == function ? DHT : GTA_MSG_RUDP == function ? RUDP : GNUTELLA; } /* * Message is ambiguous: its leading header appears to be * both a legitimate Gnutella message and a semi-reliable UDP * header. * * We have to apply some heuristics to decide whether to handle * the message as a Gnutella one or as a semi-reliable UDP one, * knowing that if we improperly classify it, the message will * not be handled correctly. * * Note that this is highly unlikely. There is about 1 chance * in 10 millions (1 / 2^23 exactly) to mis-interpret a random * Gnutella MUID as the start of one of the semi-reliable * protocols we support. Our discriminating logic probes a * few more bytes (say 2 at least) which are going to let us * decide with about 99% certainety. So mis-classification * will occur only once per billion -- a ratio which is OK. * * We could also mistakenely handle a semi-reliable UDP message * as a Gnutella one. For that to happen, the payload must * contain a field that will be exactly the message size, * a 1 / 2^32 event (since the size is 4 bytes in Gnutella). * However, if message flags are put to use for Gnutella UDP, * this ratio could lower to 1 / 2^16 and that is too large * a chance (about 1.5 in 100,000). * * So when we think an ambiguous message could be a valid * Gnutella message, we also check whether the message could * not be interpreted as a valid semi-reliable UDP one, and * we give priority to that classification if we have a match: * correct sequence number, consistent count and emitting host. * This checks roughly 3 more bytes in the message, yielding * a misclassification for about 1 / 2^(16+24) random cases. */ hops = gnutella_header_get_hops(data); ttl = gnutella_header_get_ttl(data); gnet_stats_inc_general(GNR_UDP_AMBIGUOUS); if (GNET_PROPERTY(udp_debug)) { g_debug("UDP ambiguous datagram from %s: " "%zu bytes (%u-byte payload), " "function=%u, hops=%u, TTL=%u, size=%u", host_addr_port_to_string(s->addr, s->port), len, size, function, hops, ttl, gnutella_header_get_size(data)); dump_hex(stderr, "UDP ambiguous datagram", data, len); } switch (function) { case GTA_MSG_DHT: /* * A DHT message must be larger than KDA_HEADER_SIZE bytes. */ if (len < KDA_HEADER_SIZE) break; /* Not a DHT message */ /* * DHT messages have no bits defined in the size field * to mark them. */ if (valid != GMSG_VALID) break; /* Higest bit set, not a DHT message */ /* * If it is a DHT message, it must have a valid opcode. */ function = kademlia_header_get_function(data); if (function > KDA_MSG_MAX_ID) break; /* Not a valid DHT opcode */ /* * Check the contact address length: it must be 4 in the * header, because there is only room for an IPv4 address. */ if (!kademlia_header_constants_ok(data)) break; /* Not a valid Kademlia header */ /* * Make sure we're not mistaking a valid semi-reliable UDP * message as a DHT message. */ if (udp_is_valid_semi_reliable(utp, s, data, len)) break; /* Validated it as semi-reliable UDP */ g_warning("UDP ambiguous message from %s (%zu bytes total)," " DHT function is %s", host_addr_port_to_string(s->addr, s->port), len, kmsg_name(function)); return DHT; case GTA_MSG_INIT: case GTA_MSG_PUSH_REQUEST: case GTA_MSG_SEARCH: /* * No incoming messages of this type can have a TTL * indicating a deflated payload, since there is no * guarantee the host would be able to read it (deflated * UDP is negotiated and can therefore only come from a * response). */ if (ttl & GTA_UDP_DEFLATED) break; /* Not Gnutella, we're positive */ /* FALL THROUGH */ case GTA_MSG_INIT_RESPONSE: case GTA_MSG_VENDOR: case GTA_MSG_SEARCH_RESULTS: /* * To further discriminate, look at the hop count. * Over UDP, the hop count will be low (0 or 1 mostly) * and definitely less than 3 since the only UDP-relayed * messages are from GUESS, and they can travel at most * through a leaf and an ultra node before reaching us. */ if (hops >= 3U) break; /* Gnutella is very unlikely */ /* * Check the TTL, cleared from bits that indicate * support for deflated UDP or a deflated payload. * No servent should send a TTL greater than 7, which * was the de-facto limit in the early Gnutella days. */ if ((ttl & ~(GTA_UDP_CAN_INFLATE | GTA_UDP_DEFLATED)) > 7U) break; /* Gnutella is very unlikely */ /* * Make sure we're not mistaking a valid semi-reliable UDP * message as a Gnutella message. */ if (udp_is_valid_semi_reliable(utp, s, data, len)) break; /* Validated it as semi-reliable UDP */ g_warning("UDP ambiguous message from %s (%zu bytes total)," " Gnutella function is %s, hops=%u, TTL=%u", host_addr_port_to_string(s->addr, s->port), len, gmsg_name(function), hops, ttl); return GNUTELLA; case GTA_MSG_RUDP: /* * RUDP traffic is special: the only meaningful fields * of the Gnutella header are the opcode field (which we * have read here since we fall into this case) and the * Gnutella header size. * * The TTL and hops fields cannot be interpreted to * disambiguate, so our only option is deeper inspection. */ if (udp_is_valid_semi_reliable(utp, s, data, len)) break; /* Validated it as semi-reliable UDP */ g_warning("UDP ambiguous message from %s (%zu bytes total)," " interpreted as RUDP packet", host_addr_port_to_string(s->addr, s->port), len); return RUDP; case GTA_MSG_STANDARD: /* Nobody is using this function code */ default: break; /* Not a function we expect over UDP */ } /* * Will be handled as semi-reliable UDP. */ gnet_stats_inc_general(GNR_UDP_AMBIGUOUS_AS_SEMI_RELIABLE); { udp_tag_t tag; memcpy(tag.value, data, sizeof tag.value); g_warning("UDP ambiguous message (%zu bytes total), " "not Gnutella (function is %d, hops=%u, TTL=%u) " "handling as semi-reliable UDP (tag=\"%s\")", len, function, hops, ttl, udp_tag_to_string(tag)); } return utp; } /* FALL THROUGH */ case GMSG_VALID_NO_PROCESS: case GMSG_INVALID: break; } } return utp; }