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); }
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); }
/** * 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; }