|
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 "deconstruct_unix.h"
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#include <stdio.h>
|
|
Packit |
b802ec |
#include <stdlib.h>
|
|
Packit |
b802ec |
#include <string.h>
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#include "protocols.h"
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
#define MAX_MPLS_LABELS 8
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Given an ICMP id + ICMP sequence, find the match probe we've
|
|
Packit |
b802ec |
transmitted and if found, respond to the command which sent it
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void find_and_receive_probe(
|
|
Packit |
b802ec |
struct net_state_t *net_state,
|
|
Packit |
b802ec |
const struct sockaddr_storage *remote_addr,
|
|
Packit |
b802ec |
struct timeval *timestamp,
|
|
Packit |
b802ec |
int icmp_type,
|
|
Packit |
b802ec |
int protocol,
|
|
Packit |
b802ec |
int icmp_id,
|
|
Packit |
b802ec |
int icmp_sequence,
|
|
Packit |
b802ec |
int mpls_count,
|
|
Packit |
b802ec |
struct mpls_label_t *mpls)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
struct probe_t *probe;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
probe = find_probe(net_state, protocol, icmp_id, icmp_sequence);
|
|
Packit |
b802ec |
if (probe == NULL) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
receive_probe(net_state, probe, icmp_type,
|
|
Packit |
b802ec |
remote_addr, timestamp, mpls_count, mpls);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Handle a UDP packet received embedded in an ICMP reply.
|
|
Packit |
b802ec |
The sequence number identifying the probe might be in
|
|
Packit |
b802ec |
the source port number, the destination port number, or
|
|
Packit |
b802ec |
the checksum. We'll check all three.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void handle_inner_udp_packet(
|
|
Packit |
b802ec |
struct net_state_t *net_state,
|
|
Packit |
b802ec |
const struct sockaddr_storage *remote_addr,
|
|
Packit |
b802ec |
int icmp_result,
|
|
Packit |
b802ec |
const struct UDPHeader *udp,
|
|
Packit |
b802ec |
int udp_length,
|
|
Packit |
b802ec |
struct timeval *timestamp,
|
|
Packit |
b802ec |
int mpls_count,
|
|
Packit |
b802ec |
struct mpls_label_t *mpls)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
struct probe_t *probe;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
probe = find_probe(net_state, IPPROTO_UDP, 0, udp->dstport);
|
|
Packit |
b802ec |
if (probe == NULL) {
|
|
Packit |
b802ec |
probe = find_probe(net_state, IPPROTO_UDP, 0, udp->srcport);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
if (probe == NULL) {
|
|
Packit |
b802ec |
probe = find_probe(net_state, IPPROTO_UDP, 0, udp->checksum);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (probe != NULL) {
|
|
Packit |
b802ec |
receive_probe(net_state, probe, icmp_result,
|
|
Packit |
b802ec |
remote_addr, timestamp, mpls_count, mpls);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
We've received an ICMP message with an embedded IP packet.
|
|
Packit |
b802ec |
We will try to determine which of our outgoing probes
|
|
Packit |
b802ec |
corresponds to the embedded IP packet and record the response.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void handle_inner_ip4_packet(
|
|
Packit |
b802ec |
struct net_state_t *net_state,
|
|
Packit |
b802ec |
const struct sockaddr_storage *remote_addr,
|
|
Packit |
b802ec |
int icmp_result,
|
|
Packit |
b802ec |
const struct IPHeader *ip,
|
|
Packit |
b802ec |
int packet_length,
|
|
Packit |
b802ec |
struct timeval *timestamp,
|
|
Packit |
b802ec |
int mpls_count,
|
|
Packit |
b802ec |
struct mpls_label_t *mpls)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
const int ip_icmp_size =
|
|
Packit |
b802ec |
sizeof(struct IPHeader) + sizeof(struct ICMPHeader);
|
|
Packit |
b802ec |
const int ip_udp_size =
|
|
Packit |
b802ec |
sizeof(struct IPHeader) + sizeof(struct UDPHeader);
|
|
Packit |
b802ec |
const int ip_tcp_size =
|
|
Packit |
b802ec |
sizeof(struct IPHeader) + sizeof(struct TCPHeader);
|
|
Packit |
b802ec |
const struct ICMPHeader *icmp;
|
|
Packit |
b802ec |
const struct UDPHeader *udp;
|
|
Packit |
b802ec |
const struct TCPHeader *tcp;
|
|
Packit |
b802ec |
int udp_length;
|
|
Packit |
b802ec |
#ifdef IPPROTO_SCTP
|
|
Packit |
b802ec |
const int ip_sctp_size =
|
|
Packit |
b802ec |
sizeof(struct IPHeader) + sizeof(struct SCTPHeader);
|
|
Packit |
b802ec |
const struct SCTPHeader *sctp;
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (ip->protocol == IPPROTO_ICMP) {
|
|
Packit |
b802ec |
if (packet_length < ip_icmp_size) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
icmp = (struct ICMPHeader *) (ip + 1);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
find_and_receive_probe(net_state, remote_addr, timestamp,
|
|
Packit |
b802ec |
icmp_result, IPPROTO_ICMP, icmp->id,
|
|
Packit |
b802ec |
icmp->sequence, mpls_count, mpls);
|
|
Packit |
b802ec |
} else if (ip->protocol == IPPROTO_UDP) {
|
|
Packit |
b802ec |
if (packet_length < ip_udp_size) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
udp = (struct UDPHeader *) (ip + 1);
|
|
Packit |
b802ec |
udp_length = packet_length - sizeof(struct IPHeader);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
handle_inner_udp_packet(net_state, remote_addr, icmp_result, udp,
|
|
Packit |
b802ec |
udp_length, timestamp, mpls_count, mpls);
|
|
Packit |
b802ec |
} else if (ip->protocol == IPPROTO_TCP) {
|
|
Packit |
b802ec |
if (packet_length < ip_tcp_size) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
tcp = (struct TCPHeader *) (ip + 1);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
find_and_receive_probe(net_state, remote_addr, timestamp,
|
|
Packit |
b802ec |
icmp_result, IPPROTO_TCP, 0, tcp->srcport,
|
|
Packit |
b802ec |
mpls_count, mpls);
|
|
Packit |
b802ec |
#ifdef IPPROTO_SCTP
|
|
Packit |
b802ec |
} else if (ip->protocol == IPPROTO_SCTP) {
|
|
Packit |
b802ec |
if (packet_length < ip_sctp_size) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
sctp = (struct SCTPHeader *) (ip + 1);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
find_and_receive_probe(net_state, remote_addr, timestamp,
|
|
Packit |
b802ec |
icmp_result, IPPROTO_SCTP, 0, sctp->srcport,
|
|
Packit |
b802ec |
mpls_count, mpls);
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Examine the IPv6 header embedded in a returned ICMPv6 packet
|
|
Packit |
b802ec |
in order to match it with a probe which we previously sent.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void handle_inner_ip6_packet(
|
|
Packit |
b802ec |
struct net_state_t *net_state,
|
|
Packit |
b802ec |
const struct sockaddr_storage *remote_addr,
|
|
Packit |
b802ec |
int icmp_result,
|
|
Packit |
b802ec |
const struct IP6Header *ip,
|
|
Packit |
b802ec |
int packet_length,
|
|
Packit |
b802ec |
struct timeval *timestamp,
|
|
Packit |
b802ec |
int mpls_count,
|
|
Packit |
b802ec |
struct mpls_label_t *mpls)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
const int ip_icmp_size =
|
|
Packit |
b802ec |
sizeof(struct IP6Header) + sizeof(struct ICMPHeader);
|
|
Packit |
b802ec |
const int ip_udp_size =
|
|
Packit |
b802ec |
sizeof(struct IP6Header) + sizeof(struct UDPHeader);
|
|
Packit |
b802ec |
const int ip_tcp_size =
|
|
Packit |
b802ec |
sizeof(struct IP6Header) + sizeof(struct TCPHeader);
|
|
Packit |
b802ec |
const struct ICMPHeader *icmp;
|
|
Packit |
b802ec |
const struct UDPHeader *udp;
|
|
Packit |
b802ec |
const struct TCPHeader *tcp;
|
|
Packit |
b802ec |
int udp_length;
|
|
Packit |
b802ec |
#ifdef IPPROTO_SCTP
|
|
Packit |
b802ec |
const int ip_sctp_size =
|
|
Packit |
b802ec |
sizeof(struct IPHeader) + sizeof(struct SCTPHeader);
|
|
Packit |
b802ec |
const struct SCTPHeader *sctp;
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (ip->protocol == IPPROTO_ICMPV6) {
|
|
Packit |
b802ec |
if (packet_length < ip_icmp_size) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
icmp = (struct ICMPHeader *) (ip + 1);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
find_and_receive_probe(net_state, remote_addr, timestamp,
|
|
Packit |
b802ec |
icmp_result, IPPROTO_ICMP, icmp->id,
|
|
Packit |
b802ec |
icmp->sequence, mpls_count, mpls);
|
|
Packit |
b802ec |
} else if (ip->protocol == IPPROTO_UDP) {
|
|
Packit |
b802ec |
if (packet_length < ip_udp_size) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
udp = (struct UDPHeader *) (ip + 1);
|
|
Packit |
b802ec |
udp_length = packet_length - sizeof(struct IP6Header);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
handle_inner_udp_packet(net_state, remote_addr, icmp_result, udp,
|
|
Packit |
b802ec |
udp_length, timestamp, mpls_count, mpls);
|
|
Packit |
b802ec |
} else if (ip->protocol == IPPROTO_TCP) {
|
|
Packit |
b802ec |
if (packet_length < ip_tcp_size) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
tcp = (struct TCPHeader *) (ip + 1);
|
|
Packit |
b802ec |
find_and_receive_probe(net_state, remote_addr, timestamp,
|
|
Packit |
b802ec |
icmp_result, IPPROTO_TCP, 0, tcp->srcport,
|
|
Packit |
b802ec |
mpls_count, mpls);
|
|
Packit |
b802ec |
#ifdef IPPROTO_SCTP
|
|
Packit |
b802ec |
} else if (ip->protocol == IPPROTO_SCTP) {
|
|
Packit |
b802ec |
if (packet_length < ip_sctp_size) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
sctp = (struct SCTPHeader *) (ip + 1);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
find_and_receive_probe(net_state, remote_addr, timestamp,
|
|
Packit |
b802ec |
icmp_result, IPPROTO_SCTP, 0, sctp->srcport,
|
|
Packit |
b802ec |
mpls_count, mpls);
|
|
Packit |
b802ec |
#endif
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Convert an ICMP MPLS extension object into an mpls_label_t structure */
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
int decode_mpls_object(
|
|
Packit |
b802ec |
struct ICMPExtensionObject *icmp_obj,
|
|
Packit |
b802ec |
int obj_len,
|
|
Packit |
b802ec |
struct mpls_label_t *mpls,
|
|
Packit |
b802ec |
int mpls_count)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
int label_bytes;
|
|
Packit |
b802ec |
int labels_present;
|
|
Packit |
b802ec |
int i;
|
|
Packit |
b802ec |
struct ICMPExtMPLSLabel *ext_mpls;
|
|
Packit |
b802ec |
struct ICMPExtMPLSLabel *ext_label;
|
|
Packit |
b802ec |
struct mpls_label_t *label;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
label_bytes = obj_len - sizeof(struct ICMPExtensionObject);
|
|
Packit |
b802ec |
labels_present = label_bytes / sizeof(struct ICMPExtMPLSLabel);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
ext_mpls = (struct ICMPExtMPLSLabel *) (icmp_obj + 1);
|
|
Packit |
b802ec |
for (i = 0; i < mpls_count && i < labels_present; i++) {
|
|
Packit |
b802ec |
ext_label = &ext_mpls[i];
|
|
Packit |
b802ec |
label = &mpls[i];
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
memset(label, 0, sizeof(struct mpls_label_t));
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
label->label =
|
|
Packit |
b802ec |
ext_label->label[0] << 12 |
|
|
Packit |
b802ec |
ext_label->label[1] << 4 | ext_label->label[2] >> 4;
|
|
Packit |
b802ec |
label->experimental_use = (ext_label->label[2] & 0x0E) >> 1;
|
|
Packit |
b802ec |
label->bottom_of_stack = ext_label->label[2] & 0x01;
|
|
Packit |
b802ec |
label->ttl = ext_label->ttl;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return i;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Extract MPLS labels from the ICMP extension header, if present */
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
int decode_mpls_labels(
|
|
Packit |
b802ec |
const struct ICMPHeader *icmp,
|
|
Packit |
b802ec |
int packet_length,
|
|
Packit |
b802ec |
struct mpls_label_t *mpls,
|
|
Packit |
b802ec |
int mpls_count)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
const int icmp_orig_icmp_ext_size =
|
|
Packit |
b802ec |
sizeof(struct ICMPHeader) + ICMP_ORIGINAL_DATAGRAM_MIN_SIZE +
|
|
Packit |
b802ec |
sizeof(struct ICMPExtensionHeader);
|
|
Packit |
b802ec |
char *inner_packet;
|
|
Packit |
b802ec |
char *icmp_object_bytes;
|
|
Packit |
b802ec |
struct ICMPExtensionHeader *icmp_ext;
|
|
Packit |
b802ec |
struct ICMPExtensionObject *icmp_obj;
|
|
Packit |
b802ec |
int remaining_size;
|
|
Packit |
b802ec |
int obj_len;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (packet_length < icmp_orig_icmp_ext_size) {
|
|
Packit |
b802ec |
return 0;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
inner_packet = (char *) (icmp + 1);
|
|
Packit |
b802ec |
icmp_ext = (struct ICMPExtensionHeader *)
|
|
Packit |
b802ec |
(inner_packet + ICMP_ORIGINAL_DATAGRAM_MIN_SIZE);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if ((icmp_ext->version & 0xF0) != 0x20) {
|
|
Packit |
b802ec |
return 0;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
remaining_size = packet_length - icmp_orig_icmp_ext_size;
|
|
Packit |
b802ec |
icmp_object_bytes = (char *) (icmp_ext + 1);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Iterate through the chain of extension objects, looking for
|
|
Packit |
b802ec |
an MPLS label extension.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
while (remaining_size >= sizeof(struct ICMPExtensionObject)) {
|
|
Packit |
b802ec |
icmp_obj = (struct ICMPExtensionObject *) icmp_object_bytes;
|
|
Packit |
b802ec |
obj_len = ntohs(icmp_obj->len);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (obj_len > remaining_size) {
|
|
Packit |
b802ec |
return 0;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
if (obj_len < sizeof(struct ICMPExtensionObject)) {
|
|
Packit |
b802ec |
return 0;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (icmp_obj->classnum == ICMP_EXT_MPLS_CLASSNUM &&
|
|
Packit |
b802ec |
icmp_obj->ctype == ICMP_EXT_MPLS_CTYPE) {
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return decode_mpls_object(icmp_obj, obj_len, mpls, mpls_count);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
remaining_size -= obj_len;
|
|
Packit |
b802ec |
icmp_object_bytes += obj_len;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
return 0;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Decode the ICMP header received and try to find a probe which it
|
|
Packit |
b802ec |
is in response to.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void handle_received_icmp4_packet(
|
|
Packit |
b802ec |
struct net_state_t *net_state,
|
|
Packit |
b802ec |
const struct sockaddr_storage *remote_addr,
|
|
Packit |
b802ec |
const struct ICMPHeader *icmp,
|
|
Packit |
b802ec |
int packet_length,
|
|
Packit |
b802ec |
struct timeval *timestamp)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
const int icmp_ip_size =
|
|
Packit |
b802ec |
sizeof(struct ICMPHeader) + sizeof(struct IPHeader);
|
|
Packit |
b802ec |
const struct IPHeader *inner_ip;
|
|
Packit |
b802ec |
int inner_size = packet_length - sizeof(struct ICMPHeader);
|
|
Packit |
b802ec |
int mpls_count;
|
|
Packit |
b802ec |
struct mpls_label_t mpls[MAX_MPLS_LABELS];
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
mpls_count =
|
|
Packit |
b802ec |
decode_mpls_labels(icmp, packet_length, mpls, MAX_MPLS_LABELS);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* If we get an echo reply, our probe reached the destination host */
|
|
Packit |
b802ec |
if (icmp->type == ICMP_ECHOREPLY) {
|
|
Packit |
b802ec |
find_and_receive_probe(net_state, remote_addr, timestamp,
|
|
Packit |
b802ec |
ICMP_ECHOREPLY, IPPROTO_ICMP, icmp->id,
|
|
Packit |
b802ec |
icmp->sequence, mpls_count, mpls);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (packet_length < icmp_ip_size) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
inner_ip = (struct IPHeader *) (icmp + 1);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
If we get a time exceeded, we got a response from an intermediate
|
|
Packit |
b802ec |
host along the path to our destination.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
if (icmp->type == ICMP_TIME_EXCEEDED) {
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
The IP packet inside the ICMP response contains our original
|
|
Packit |
b802ec |
IP header. That's where we can get our original ID and
|
|
Packit |
b802ec |
sequence number.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
handle_inner_ip4_packet(net_state, remote_addr,
|
|
Packit |
b802ec |
ICMP_TIME_EXCEEDED, inner_ip, inner_size,
|
|
Packit |
b802ec |
timestamp, mpls_count, mpls);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (icmp->type == ICMP_DEST_UNREACH) {
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
We'll get a ICMP_PORT_UNREACH when a non-ICMP probe
|
|
Packit |
b802ec |
reaches its final destination. (Assuming that port isn't
|
|
Packit |
b802ec |
open on the destination host.)
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
if (icmp->code == ICMP_PORT_UNREACH) {
|
|
Packit |
b802ec |
handle_inner_ip4_packet(net_state, remote_addr,
|
|
Packit |
b802ec |
ICMP_ECHOREPLY, inner_ip, inner_size,
|
|
Packit |
b802ec |
timestamp, mpls_count, mpls);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Decode the ICMPv6 header. The code duplication with ICMPv4 is
|
|
Packit |
b802ec |
unfortunate, but small details in structure size and ICMP
|
|
Packit |
b802ec |
constants differ.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
static
|
|
Packit |
b802ec |
void handle_received_icmp6_packet(
|
|
Packit |
b802ec |
struct net_state_t *net_state,
|
|
Packit |
b802ec |
const struct sockaddr_storage *remote_addr,
|
|
Packit |
b802ec |
const struct ICMPHeader *icmp,
|
|
Packit |
b802ec |
int packet_length,
|
|
Packit |
b802ec |
struct timeval *timestamp)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
const int icmp_ip_size =
|
|
Packit |
b802ec |
sizeof(struct ICMPHeader) + sizeof(struct IP6Header);
|
|
Packit |
b802ec |
const struct IP6Header *inner_ip;
|
|
Packit |
b802ec |
int inner_size = packet_length - sizeof(struct ICMPHeader);
|
|
Packit |
b802ec |
int mpls_count;
|
|
Packit |
b802ec |
struct mpls_label_t mpls[MAX_MPLS_LABELS];
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
mpls_count =
|
|
Packit |
b802ec |
decode_mpls_labels(icmp, packet_length, mpls, MAX_MPLS_LABELS);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (icmp->type == ICMP6_ECHOREPLY) {
|
|
Packit |
b802ec |
find_and_receive_probe(net_state, remote_addr, timestamp,
|
|
Packit |
b802ec |
ICMP_ECHOREPLY, IPPROTO_ICMP, icmp->id,
|
|
Packit |
b802ec |
icmp->sequence, mpls_count, mpls);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (packet_length < icmp_ip_size) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
inner_ip = (struct IP6Header *) (icmp + 1);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (icmp->type == ICMP6_TIME_EXCEEDED) {
|
|
Packit |
b802ec |
handle_inner_ip6_packet(net_state, remote_addr,
|
|
Packit |
b802ec |
ICMP_TIME_EXCEEDED, inner_ip, inner_size,
|
|
Packit |
b802ec |
timestamp, mpls_count, mpls);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
if (icmp->type == ICMP6_DEST_UNREACH) {
|
|
Packit |
b802ec |
if (icmp->code == ICMP6_PORT_UNREACH) {
|
|
Packit |
b802ec |
handle_inner_ip6_packet(net_state, remote_addr,
|
|
Packit |
b802ec |
ICMP_ECHOREPLY, inner_ip, inner_size,
|
|
Packit |
b802ec |
timestamp, mpls_count, mpls);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
We've received a new IPv4 ICMP packet.
|
|
Packit |
b802ec |
We'll check to see that it is a response to one of our probes, and
|
|
Packit |
b802ec |
if so, report the result of the probe to our command stream.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
void handle_received_ip4_packet(
|
|
Packit |
b802ec |
struct net_state_t *net_state,
|
|
Packit |
b802ec |
const struct sockaddr_storage *remote_addr,
|
|
Packit |
b802ec |
const void *packet,
|
|
Packit |
b802ec |
int packet_length,
|
|
Packit |
b802ec |
struct timeval *timestamp)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
const int ip_icmp_size =
|
|
Packit |
b802ec |
sizeof(struct IPHeader) + sizeof(struct ICMPHeader);
|
|
Packit |
b802ec |
const struct IPHeader *ip;
|
|
Packit |
b802ec |
const struct ICMPHeader *icmp;
|
|
Packit |
b802ec |
int icmp_length;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/* Ensure that we don't access memory beyond the bounds of the packet */
|
|
Packit |
b802ec |
if (packet_length < ip_icmp_size) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
ip = (struct IPHeader *) packet;
|
|
Packit |
b802ec |
if (ip->protocol != IPPROTO_ICMP) {
|
|
Packit |
b802ec |
return;
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
icmp = (struct ICMPHeader *) (ip + 1);
|
|
Packit |
b802ec |
icmp_length = packet_length - sizeof(struct IPHeader);
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
handle_received_icmp4_packet(net_state, remote_addr, icmp, icmp_length,
|
|
Packit |
b802ec |
timestamp);
|
|
Packit |
b802ec |
}
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
/*
|
|
Packit |
b802ec |
Unlike ICMPv6 raw sockets, unlike ICMPv4, don't include the IP header
|
|
Packit |
b802ec |
in received packets, so we can assume the packet we got starts
|
|
Packit |
b802ec |
with the ICMP packet.
|
|
Packit |
b802ec |
*/
|
|
Packit |
b802ec |
void handle_received_ip6_packet(
|
|
Packit |
b802ec |
struct net_state_t *net_state,
|
|
Packit |
b802ec |
const struct sockaddr_storage *remote_addr,
|
|
Packit |
b802ec |
const void *packet,
|
|
Packit |
b802ec |
int packet_length,
|
|
Packit |
b802ec |
struct timeval *timestamp)
|
|
Packit |
b802ec |
{
|
|
Packit |
b802ec |
const struct ICMPHeader *icmp;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
icmp = (struct ICMPHeader *) packet;
|
|
Packit |
b802ec |
|
|
Packit |
b802ec |
handle_received_icmp6_packet(net_state, remote_addr, icmp,
|
|
Packit |
b802ec |
packet_length, timestamp);
|
|
Packit |
b802ec |
}
|