Ejemplo n.º 1
0
bool network_tag_probe(network_t * network, probe_t * probe)
{
    uint16_t   tag,         // Network-side endianness
               checksum;    // Host-side endianness
    size_t     payload_size = probe_get_payload_size(probe);
    size_t     tag_size     = sizeof(uint16_t);
    size_t     num_layers   = probe_get_num_layers(probe);

    // For probes having a payload of size 0 and a "body" field (like icmp)
    layer_t  * last_layer;
    bool       tag_in_body = false;

    /* The probe gets assigned a unique tag. Currently we encode it in the UDP
     * checksum, but I guess the tag will be protocol dependent. Also, since the
     * tag changes the probe, the user has no direct control of what is sent on
     * the wire, since typically a packet is tagged just before sending, after
     * scheduling, to maximize the number of probes in flight. Available space
     * in the headers is used for tagging, + encoding some information... */
    /* XXX hardcoded XXX */

    /* 1) Set payload = tag : this is only possible if both the payload and the
     * checksum have not been set by the user.
     * We need a list of used tags = in flight... + an efficient way to get a
     * free one... Also, we need to share tags between several instances ?
     * randomized tags ? Also, we need to determine how much size we have to
     * encode information. */

    if (num_layers < 2 || !(last_layer = probe_get_layer(probe, num_layers - 2))) {
        fprintf(stderr, "network_tag_probe: not enough layer (num_layers = %d)\n", (unsigned int)num_layers);
        goto ERR_GET_LAYER;
    }

    // The last layer is the payload, the previous one is the last protocol layer.
    // If this layer has a "body" field like icmp, we have no payload and
    // we use the body field.
    if (last_layer->protocol && protocol_get_field(last_layer->protocol, "body")) {
        tag_in_body = true;
    }

    tag = htons(network_get_available_tag(network));

    // Write the tag at offset zero of the payload
    if (tag_in_body) {
        probe_write_field(probe, "body", &tag, tag_size);
    } else {
        if (payload_size < tag_size) {
            fprintf(stderr, "Payload too short (payload_size = %u tag_size = %u)\n", (unsigned int)payload_size, (unsigned int)tag_size);
            goto ERR_INVALID_PAYLOAD;
        }

        if (!(probe_write_payload(probe, &tag, tag_size))) {
            goto ERR_PROBE_WRITE_PAYLOAD;
        }
    }

    // Fix checksum to get a well-formed packet
    if (!(probe_update_checksum(probe))) {
        fprintf(stderr, "Can't update fields\n");
        goto ERR_PROBE_UPDATE_FIELDS;
    }

    // Retrieve the checksum of UDP/TCP/ICMP checksum (host-side endianness)
    if (!(probe_extract_tag(probe, &checksum))) {
        fprintf(stderr, "Can't extract tag\n");
        goto ERR_PROBE_EXTRACT_CHECKSUM;
    }

    // Write the probe ID in the UDP/TCP/ICMP checksum
    if (!(probe_set_tag(probe, ntohs(tag)))) {
        fprintf(stderr, "Can't set tag\n");
        goto ERR_PROBE_SET_TAG;
    }

    // Write the tag using the network-side endianness in the payload
    checksum = htons(checksum);
    if (tag_in_body) {
        if (!(probe_write_field(probe, "body", &checksum, tag_size))) {
            fprintf(stderr, "Can't set body\n");
            goto ERR_PROBE_SET_FIELD;
        }
    } else {
        if (!probe_write_payload(probe, &checksum, tag_size)) {
            fprintf(stderr, "Can't write payload (2)\n");
            goto ERR_BUFFER_WRITE_BYTES2;
        }
    }

    return true;

ERR_PROBE_SET_FIELD:
ERR_BUFFER_WRITE_BYTES2:
ERR_PROBE_SET_TAG:
ERR_PROBE_EXTRACT_CHECKSUM:
ERR_PROBE_UPDATE_FIELDS:
ERR_PROBE_WRITE_PAYLOAD:
ERR_INVALID_PAYLOAD:
ERR_GET_LAYER:
    return false;
}
int main(int argc, char ** argv)
{
    algorithm_instance_t * instance;
    traceroute_options_t   options = traceroute_get_default_options();
    probe_t              * probe;
    pt_loop_t            * loop;
    int                    family;
    int                    ret = EXIT_FAILURE;
    const char           * ip_protocol_name;
    const char           * protocol_name;
    address_t              dst_addr;

    // Harcoded command line parsing here
    char dst_ip[] = "8.8.8.8";
    //char dst_ip[] = "1.1.1.2";
    //char dst_ip[] = "2001:db8:85a3::8a2e:370:7338";

    if (!address_guess_family(dst_ip, &family)) {
        fprintf(stderr, "Cannot guess family of destination address (%s)", dst_ip);
        goto ERR_ADDRESS_GUESS_FAMILY;
    }

    if (address_from_string(family, dst_ip, &dst_addr) != 0) {
        fprintf(stderr, "Cannot guess family of destination address (%s)", dst_ip);
        goto ERR_ADDRESS_FROM_STRING;
    }

    // Prepare options related to the 'traceroute' algorithm
    options.do_resolv = false;
    options.dst_addr = &dst_addr;
    options.num_probes = 1;
//    options.max_ttl = 1;
    printf("num_probes = %zu max_ttl = %u\n", options.num_probes, options.max_ttl);

    // Create libparistraceroute loop
    // No information shared by traceroute algorithm instances, so we pass NULL
    if (!(loop = pt_loop_create(loop_handler, NULL))) {
        fprintf(stderr, "Cannot create libparistraceroute loop");
        goto ERR_LOOP_CREATE;
    }

    // Probe skeleton definition: IPv4/UDP probe targetting 'dst_ip'
    if (!(probe = probe_create())) {
        fprintf(stderr, "Cannot create probe skeleton");
        goto ERR_PROBE_CREATE;
    }

    switch (family) {
        case AF_INET:
            ip_protocol_name = "ipv4";
            protocol_name    = "icmpv4";
            break;
        case AF_INET6:
            ip_protocol_name = "ipv6";
            protocol_name    = "icmpv6";
            break;
        default:
            fprintf(stderr, "Internet family not supported (%d)\n", family);
            goto ERR_FAMILY;
    }
//    protocol_name = "udp";
    protocol_name = "tcp";
    printf("protocol_name = %s\n", protocol_name);

    if (!probe_set_protocols(probe, ip_protocol_name, protocol_name, NULL)) {
        fprintf(stderr, "Can't set protocols %s/%s\n", ip_protocol_name, protocol_name);
        goto ERR_PROBE_SET_PROTOCOLS;
    }

    if (strncmp("icmp", protocol_name, 4) != 0) {
        probe_write_payload(probe, "\0\0\0\0", 4);
    } else {
        probe_set_field(probe, I32("body", 1));
    }

    probe_set_fields(probe,
        ADDRESS("dst_ip", &dst_addr),
        I16("dst_port", 3000),
        NULL
    );

    /*
    if (strcmp("tcp", protocol_name) == 0) {
        printf("setting tcp fields\n");
        uint8_t one = 1;
        probe_set_fields(probe,
            BITS("reserved", 1, &one),
            BITS("ns",  1, &one),
            BITS("cwr", 1, &one),
            BITS("ece", 1, &one),
            BITS("urg", 1, &one),
            BITS("ack", 1, &one),
            BITS("psh", 1, &one),
            BITS("rst", 1, &one),
            BITS("syn", 1, &one),
            BITS("fin", 1, &one),
            NULL
        );
    }
    */
    probe_dump(probe);

    // Instanciate a 'traceroute' algorithm
    if (!(instance = pt_add_instance(loop, "traceroute", &options, probe))) {
        fprintf(stderr, "Cannot add 'traceroute' algorithm");
        goto ERR_INSTANCE;
    }

    // Wait for events. They will be catched by handler_user()
    if (pt_loop(loop, 0) < 0) {
        fprintf(stderr, "Main loop interrupted");
        goto ERR_IN_PT_LOOP;
    }
    ret = EXIT_SUCCESS;

    // Free data and quit properly
ERR_IN_PT_LOOP:
    // instance is freed by pt_loop_free
ERR_INSTANCE:
ERR_PROBE_SET_PROTOCOLS:
ERR_FAMILY:
    probe_free(probe);
ERR_PROBE_CREATE:
    pt_loop_free(loop);
ERR_LOOP_CREATE:
ERR_ADDRESS_FROM_STRING:
ERR_ADDRESS_GUESS_FAMILY:
    exit(ret);
}