|
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 |
}
|