Blame test/packet_listen.c

Packit b802ec
/*
Packit b802ec
    mtr  --  a network diagnostic tool
Packit b802ec
    Copyright (C) 2016  Matt Kimball
Packit b802ec
Packit b802ec
    This program is free software; you can redistribute it and/or modify
Packit b802ec
    it under the terms of the GNU General Public License version 2 as
Packit b802ec
    published by the Free Software Foundation.
Packit b802ec
Packit b802ec
    This program is distributed in the hope that it will be useful,
Packit b802ec
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit b802ec
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit b802ec
    GNU General Public License for more details.
Packit b802ec
Packit b802ec
    You should have received a copy of the GNU General Public License
Packit b802ec
    along with this program; if not, write to the Free Software
Packit b802ec
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Packit b802ec
*/
Packit b802ec
Packit b802ec
#include <netinet/in.h>
Packit b802ec
#include <stdbool.h>
Packit b802ec
#include <stdio.h>
Packit b802ec
#include <stdlib.h>
Packit b802ec
#include <sys/socket.h>
Packit b802ec
#include <unistd.h>
Packit b802ec
Packit b802ec
#include "packet/protocols.h"
Packit b802ec
Packit b802ec
#define MAX_PACKET_SIZE 9000
Packit b802ec
Packit b802ec
/*
Packit b802ec
    The first probe sent by mtr-packet will have this sequence number,
Packit b802ec
    so wait for an ICMP packet with this sequence ID.
Packit b802ec
*/
Packit b802ec
#define SEQUENCE_NUM 33000
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Check to see if the packet we've recieved is intended for this test
Packit b802ec
    process.  We expected the ICMP sequence number to be equal to our
Packit b802ec
    process ID.
Packit b802ec
*/
Packit b802ec
bool is_packet_for_us4(
Packit b802ec
    char *packet,
Packit b802ec
    int packet_size)
Packit b802ec
{
Packit b802ec
    int ip_icmp_size = sizeof(struct IPHeader) + sizeof(struct ICMPHeader);
Packit b802ec
    int expected_sequence;
Packit b802ec
    struct IPHeader *ip;
Packit b802ec
    struct ICMPHeader *icmp;
Packit b802ec
Packit b802ec
    if (packet_size < ip_icmp_size) {
Packit b802ec
        return false;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    ip = (struct IPHeader *)packet;
Packit b802ec
    icmp = (struct ICMPHeader *)(ip + 1);
Packit b802ec
Packit b802ec
    expected_sequence = htons(SEQUENCE_NUM);
Packit b802ec
    if (icmp->sequence == expected_sequence) {
Packit b802ec
        return true;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    return false;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Check to see if the ICMPv6 packet is for us.
Packit b802ec
    Unlike ICMPv4 packets, ICMPv6 packets don't include the IP header.
Packit b802ec
*/
Packit b802ec
bool is_packet_for_us6(
Packit b802ec
    char *packet,
Packit b802ec
    int packet_size)
Packit b802ec
{
Packit b802ec
    int expected_sequence;
Packit b802ec
    struct ICMPHeader *icmp;
Packit b802ec
Packit b802ec
    if (packet_size < sizeof(struct ICMPHeader)) {
Packit b802ec
        return false;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    icmp = (struct ICMPHeader *)packet;
Packit b802ec
Packit b802ec
    expected_sequence = htons(SEQUENCE_NUM);
Packit b802ec
    if (icmp->sequence == expected_sequence) {
Packit b802ec
        return true;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    return false;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    Check that all the bytes in the body of the packet have the same value.
Packit b802ec
    If so, return that value.  If not, return -1.
Packit b802ec
*/
Packit b802ec
int get_packet_pattern(
Packit b802ec
    unsigned char *packet,
Packit b802ec
    int packet_size)
Packit b802ec
{
Packit b802ec
    int fill_value;
Packit b802ec
    int i;
Packit b802ec
Packit b802ec
    if (packet_size <= 0) {
Packit b802ec
        return -1;
Packit b802ec
    }
Packit b802ec
Packit b802ec
    fill_value = packet[0];
Packit b802ec
    for (i = 1; i < packet_size; i++) {
Packit b802ec
        if (packet[i] != fill_value) {
Packit b802ec
            return -1;
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
Packit b802ec
    return fill_value;
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Print information about the ICMPv4 packet we received  */
Packit b802ec
void dump_packet_info4(
Packit b802ec
    char *packet,
Packit b802ec
    int packet_size)
Packit b802ec
{
Packit b802ec
    int ip_icmp_size = sizeof(struct IPHeader) + sizeof(struct ICMPHeader);
Packit b802ec
    int pattern;
Packit b802ec
    struct IPHeader *ip;
Packit b802ec
    struct ICMPHeader *icmp;
Packit b802ec
    unsigned char *body;
Packit b802ec
    int body_size;
Packit b802ec
Packit b802ec
    ip = (struct IPHeader *)packet;
Packit b802ec
    icmp = (struct ICMPHeader *)(ip + 1);
Packit b802ec
    body = (unsigned char *)(icmp + 1);
Packit b802ec
    body_size = packet_size - ip_icmp_size;
Packit b802ec
Packit b802ec
    printf("size %d\n", packet_size);
Packit b802ec
    printf("tos %d\n", ip->tos);
Packit b802ec
Packit b802ec
    pattern = get_packet_pattern(body, body_size);
Packit b802ec
    if (pattern < 0) {
Packit b802ec
        printf("bitpattern none\n");
Packit b802ec
    } else {
Packit b802ec
        printf("bitpattern %d\n", pattern);
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Print information about an ICMPv6 packet  */
Packit b802ec
void dump_packet_info6(
Packit b802ec
    char *packet,
Packit b802ec
    int packet_size)
Packit b802ec
{
Packit b802ec
    int pattern;
Packit b802ec
    struct ICMPHeader *icmp;
Packit b802ec
    unsigned char *body;
Packit b802ec
    int body_size;
Packit b802ec
    int total_size;
Packit b802ec
Packit b802ec
    icmp = (struct ICMPHeader *)packet;
Packit b802ec
    body = (unsigned char *)(icmp + 1);
Packit b802ec
    body_size = packet_size - sizeof(struct ICMPHeader);
Packit b802ec
Packit b802ec
    total_size = packet_size + sizeof(struct IP6Header);
Packit b802ec
    printf("size %d\n", total_size);
Packit b802ec
Packit b802ec
    pattern = get_packet_pattern(body, body_size);
Packit b802ec
    if (pattern < 0) {
Packit b802ec
        printf("bitpattern none\n");
Packit b802ec
    } else {
Packit b802ec
        printf("bitpattern %d\n", pattern);
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Receive ICMP packets until we get one intended for this test process  */
Packit b802ec
void loop_on_receive(
Packit b802ec
    int icmp_socket,
Packit b802ec
    int ip_version)
Packit b802ec
{
Packit b802ec
    int packet_size;
Packit b802ec
    char packet[MAX_PACKET_SIZE];
Packit b802ec
Packit b802ec
    while (true) {
Packit b802ec
        packet_size = recv(icmp_socket, packet, MAX_PACKET_SIZE, 0);
Packit b802ec
        if (packet_size < -1) {
Packit b802ec
            perror("Failure during receive");
Packit b802ec
            exit(EXIT_FAILURE);
Packit b802ec
        }
Packit b802ec
Packit b802ec
        if (ip_version == 6) {
Packit b802ec
            if (is_packet_for_us6(packet, packet_size)) {
Packit b802ec
                dump_packet_info6(packet, packet_size);
Packit b802ec
                return;
Packit b802ec
            }
Packit b802ec
        } else {
Packit b802ec
            if (is_packet_for_us4(packet, packet_size)) {
Packit b802ec
                dump_packet_info4(packet, packet_size);
Packit b802ec
                return;
Packit b802ec
            }
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*  Parse the commandline arguments  */
Packit b802ec
void parse_cmdline(
Packit b802ec
    int argc,
Packit b802ec
    char **argv,
Packit b802ec
    int *ip_version)
Packit b802ec
{
Packit b802ec
    int opt;
Packit b802ec
Packit b802ec
    *ip_version = 4;
Packit b802ec
Packit b802ec
    while ((opt = getopt(argc, argv, "46")) != -1) {
Packit b802ec
        if (opt == '4') {
Packit b802ec
            *ip_version = 4;
Packit b802ec
        }
Packit b802ec
Packit b802ec
        if (opt == '6') {
Packit b802ec
            *ip_version = 6;
Packit b802ec
        }
Packit b802ec
    }
Packit b802ec
}
Packit b802ec
Packit b802ec
/*
Packit b802ec
    A helper for mtr-packet testing which waits for an ICMP packet
Packit b802ec
    intended for this test process, and then prints information about
Packit b802ec
    it.
Packit b802ec
*/
Packit b802ec
int main(
Packit b802ec
    int argc,
Packit b802ec
    char **argv)
Packit b802ec
{
Packit b802ec
    int icmp_socket;
Packit b802ec
    int ip_version;
Packit b802ec
Packit b802ec
    parse_cmdline(argc, argv, &ip_version);
Packit b802ec
Packit b802ec
    if (ip_version == 6) {
Packit b802ec
        icmp_socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
Packit b802ec
    } else {
Packit b802ec
        icmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
Packit b802ec
    }
Packit b802ec
    if (icmp_socket < 0) {
Packit b802ec
        perror("Failure opening listening socket");
Packit b802ec
        exit(EXIT_FAILURE);
Packit b802ec
    }
Packit b802ec
Packit b802ec
    printf("status listening\n");
Packit b802ec
    fflush(stdout);
Packit b802ec
Packit b802ec
    loop_on_receive(icmp_socket, ip_version);
Packit b802ec
Packit b802ec
    return EXIT_SUCCESS;
Packit b802ec
}