1
0
mirror of https://github.com/cmand/yarrp.git synced 2024-05-11 05:55:06 +00:00
Files
cmand-yarrp/icmp.cpp
Robert Beverly bcf5f33f6d Fixes for FreeBSD version >=11:
ip(4): "Before FreeBSD 11.0 packets received on raw IP sockets had the ip_len and
     ip_off fields converted to	host byte order.  Packets written to raw IP
     sockets were expected to have ip_len and ip_off in	host byte order."
2020-06-27 09:30:41 -07:00

381 lines
14 KiB
C++

/****************************************************************************
Program: $Id: listener.cpp 40 2016-01-02 18:54:39Z rbeverly $
Date: $Date: 2016-01-02 10:54:39 -0800 (Sat, 02 Jan 2016) $
Description: yarrp listener thread
****************************************************************************/
#include "yarrp.h"
ICMP::ICMP() :
rtt(0), ttl(0), type(0), code(0), length(0), quote_p(0), sport(0), dport(0), ipid(0),
probesize(0), replysize(0), replyttl(0), replytos(0)
{
gettimeofday(&tv, NULL);
mpls_stack = NULL;
}
ICMP4::ICMP4(struct ip *ip, struct icmp *icmp, uint32_t elapsed, bool _coarse): ICMP()
{
coarse = _coarse;
memset(&ip_src, 0, sizeof(struct in_addr));
type = (uint8_t) icmp->icmp_type;
code = (uint8_t) icmp->icmp_code;
ip_src = ip->ip_src;
#if defined(_BSD) && !defined(_NEW_FBSD)
replysize = ip->ip_len;
#else
replysize = ntohs(ip->ip_len);
#endif
ipid = ntohs(ip->ip_id);
replytos = ip->ip_tos;
replyttl = ip->ip_ttl;
unsigned char *ptr = NULL;
quote = NULL;
if (((type == ICMP_TIMXCEED) and (code == ICMP_TIMXCEED_INTRANS)) or
(type == ICMP_UNREACH)) {
ptr = (unsigned char *) icmp;
quote = (struct ip *) (ptr + 8);
quote_p = quote->ip_p;
#if defined(_BSD) && !defined(_NEW_FBSD)
probesize = quote->ip_len;
#else
probesize = ntohs(quote->ip_len);
#endif
ttl = (ntohs(quote->ip_id)) & 0xFF;
instance = (ntohs(quote->ip_id) >> 8) & 0xFF;
/* Original probe was TCP */
if (quote->ip_p == IPPROTO_TCP) {
struct tcphdr *tcp = (struct tcphdr *) (ptr + 8 + (quote->ip_hl << 2));
rtt = elapsed - ntohl(tcp->th_seq);
if (elapsed < ntohl(tcp->th_seq))
cerr << "** RTT decode, elapsed: " << elapsed << " encoded: " << ntohl(tcp->th_seq) << endl;
sport = ntohs(tcp->th_sport);
dport = ntohs(tcp->th_dport);
}
/* Original probe was UDP */
else if (quote->ip_p == IPPROTO_UDP) {
struct udphdr *udp = (struct udphdr *) (ptr + 8 + (quote->ip_hl << 2));
/* recover timestamp from UDP.check and UDP.payloadlen */
int payloadlen = ntohs(udp->uh_ulen) - sizeof(struct icmp);
int timestamp = udp->uh_sum;
sport = ntohs(udp->uh_sport);
dport = ntohs(udp->uh_dport);
if (payloadlen > 2)
timestamp += (payloadlen-2) << 16;
if (elapsed >= timestamp) {
rtt = elapsed - timestamp;
/* checksum was 0x0000 and because of RFC, 0xFFFF was transmitted
* causing us to see packet as being 65 (2^{16}/1000) seconds in future */
} else if (udp->uh_sum == 0xffff) {
timestamp = (payloadlen-2) << 16;
rtt = elapsed - timestamp;
}
if (elapsed < timestamp) {
cerr << "** RTT decode, elapsed: " << elapsed << " encoded: " << timestamp << endl;
sport = dport = 0;
}
}
/* Original probe was ICMP */
else if (quote->ip_p == IPPROTO_ICMP) {
struct icmp *icmp = (struct icmp *) (ptr + 8 + (quote->ip_hl << 2));
uint32_t timestamp = ntohs(icmp->icmp_id);
timestamp += ntohs(icmp->icmp_seq) << 16;
rtt = elapsed - timestamp;
sport = icmp->icmp_cksum;
}
/* According to Malone PAM 2007, 2% of replies have bad IP dst. */
uint16_t sum = in_cksum((unsigned short *)&(quote->ip_dst), 4);
if (sport != sum) {
cerr << "** IP dst in ICMP reply quote invalid!" << endl;
sport = dport = 0;
}
/* Finally, does this ICMP packet have an extension (RFC4884)? */
length = (ntohl(icmp->icmp_void) & 0x00FF0000) >> 16;
length *= 4;
if ( (length > 0) and (replysize > length+8) ) {
//printf("*** ICMP Extension %d/%d\n", length, replysize);
ptr = (unsigned char *) icmp;
ptr += length+8;
if (length < 128)
ptr += (128-length);
// ptr at start of ICMP extension
ptr += 4;
// ptr at start of MPLS stack header
ptr += 2;
// is this a class/type 1/1 (MPLS)?
if ( (*ptr == 0x01) and (*(ptr+1) == 0x01) ) {
ptr += 2;
uint32_t *tmp;
mpls_label_t *lse = (mpls_label_t *) calloc(1, sizeof(mpls_label_t) );
mpls_stack = lse;
for (int labels = 0; labels < MAX_MPLS_STACK_HEIGHT; labels++) {
tmp = (uint32_t *) ptr;
if (labels > 0) {
mpls_label_t *nextlse = (mpls_label_t *) calloc(1, sizeof(mpls_label_t) );
lse->next = nextlse;
lse = nextlse;
}
lse->label = (htonl(*tmp) & 0xFFFFF000) >> 12;
lse->exp = (htonl(*tmp) & 0x00000F00) >> 8;
lse->ttl = (htonl(*tmp) & 0x000000FF);
// bottom of stack?
if (lse->exp & 0x01)
break;
ptr+=4;
}
}
}
}
}
/**
* Create ICMP6 object on received response.
*
* @param ip Received IPv6 hdr
* @param icmp Received ICMP6 hdr
* @param elapsed Total running time
*/
ICMP6::ICMP6(struct ip6_hdr *ip, struct icmp6_hdr *icmp, uint32_t elapsed, bool _coarse) : ICMP()
{
is_yarrp = false;
coarse = _coarse;
memset(&ip_src, 0, sizeof(struct in6_addr));
type = (uint8_t) icmp->icmp6_type;
code = (uint8_t) icmp->icmp6_code;
ip_src = ip->ip6_src;
replysize = ntohs(ip->ip6_plen);
replyttl = ip->ip6_hlim;
/* Ethernet
* IPv6 hdr
* ICMP6 hdr struct icmp6_hdr *icmp; <- ptr
* IPv6 hdr struct ip6_hdr *icmpip;
* Ext hdr struct ip6_ext *eh; (if present)
* Probe transport hdr struct tcphdr,udphdr,icmp6_hdr;
* Yarrp payload struct ypayload *qpayload;
*/
unsigned char *ptr = (unsigned char *) icmp;
quote = (struct ip6_hdr *) (ptr + sizeof(struct icmp6_hdr)); /* Quoted IPv6 hdr */
struct ip6_ext *eh = NULL; /* Pointer to any extension header */
struct ypayload *qpayload = NULL; /* Quoted ICMPv6 yrp payload */
uint16_t ext_hdr_len = 0;
quote_p = quote->ip6_nxt;
int offset = 0;
if (icmp->icmp6_type == ICMP6_ECHO_REPLY) {
qpayload = (struct ypayload *) (ptr + sizeof(struct icmp6_hdr));
} else {
// handle hop-by-hop (0), dest (60) and frag (44) extension headers
if ( (quote_p == 0) or (quote_p == 44) or (quote_p == 60) ) {
eh = (struct ip6_ext *) (ptr + sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr) );
ext_hdr_len = 8;
quote_p = eh->ip6e_nxt;
}
// continue processing
offset = sizeof(struct icmp6_hdr) + sizeof(struct ip6_hdr) + ext_hdr_len;
if (quote_p == IPPROTO_TCP) {
qpayload = (struct ypayload *) (ptr + offset + sizeof(struct tcphdr));
} else if (quote_p == IPPROTO_UDP) {
qpayload = (struct ypayload *) (ptr + offset + sizeof(struct udphdr));
} else if (quote_p == IPPROTO_ICMPV6) {
qpayload = (struct ypayload *) (ptr + offset + sizeof(struct icmp6_hdr));
} else {
warn("unknown quote\n");
return;
}
}
if (ntohl(qpayload->id) == 0x79727036)
is_yarrp = true;
ttl = qpayload->ttl;
instance = qpayload->instance;
yarrp_target = &(qpayload->target);
uint32_t diff = qpayload->diff;
if (elapsed >= diff)
rtt = elapsed - diff;
else
cerr << "** RTT decode, elapsed: " << elapsed << " encoded: " << diff << endl;
/* ICMP6 echo replies only quote the yarrp payload, not the full packet! */
if (((type == ICMP6_TIME_EXCEEDED) and (code == ICMP6_TIME_EXCEED_TRANSIT)) or
(type == ICMP6_DST_UNREACH)) {
probesize = ntohs(quote->ip6_plen);
if (quote_p == IPPROTO_TCP) {
struct tcphdr *tcp = (struct tcphdr *) (ptr + offset);
sport = ntohs(tcp->th_sport);
dport = ntohs(tcp->th_dport);
} else if (quote_p == IPPROTO_UDP) {
struct udphdr *udp = (struct udphdr *) (ptr + offset);
sport = ntohs(udp->uh_sport);
dport = ntohs(udp->uh_dport);
} else if (quote_p == IPPROTO_ICMPV6) {
struct icmp6_hdr *icmp6 = (struct icmp6_hdr *) (ptr + offset);
sport = ntohs(icmp6->icmp6_id);
dport = ntohs(icmp6->icmp6_seq);
}
uint16_t sum = in_cksum((unsigned short *)&(quote->ip6_dst), 16);
if (sport != sum) {
cerr << "** IP6 dst in ICMP6 reply quote invalid!" << endl;
sport = dport = 0;
}
}
}
uint32_t ICMP4::quoteDst() {
if ((type == ICMP_TIMXCEED) and (code == ICMP_TIMXCEED_INTRANS)) {
return quote->ip_dst.s_addr;
}
return 0;
}
void ICMP::printterse(char *src) {
float r = 0.0;
coarse ? r = rtt/1.0 : r = rtt/1000.0;
printf(">> ICMP response: %s Type: %d Code: %d TTL: %d RTT: %2.3fms",
src, type, code, ttl, r);
if (instance)
printf(" Inst: %u", instance);
printf("\n");
}
void ICMP::print(char *src, char *dst, int sum) {
printf("\ttype: %d code: %d from: %s\n", type, code, src);
printf("\tYarrp instance: %u\n", instance);
printf("\tTS: %lu.%ld\n", tv.tv_sec, (long) tv.tv_usec);
if (coarse)
printf("\tRTT: %u ms\n", rtt);
else
printf("\tRTT: %u us\n", rtt);
printf("\tProbe dst: %s\n", dst);
printf("\tProbe TTL: %d\n", ttl);
if (ipid) printf("\tReply IPID: %d\n", ipid);
if (quote_p) printf("\tQuoted Protocol: %d\n", quote_p);
if ( (quote_p == IPPROTO_TCP) || (quote_p == IPPROTO_UDP) )
printf("\tProbe TCP/UDP src/dst port: %d/%d\n", sport, dport);
if ( (quote_p == IPPROTO_ICMP) || (quote_p == IPPROTO_ICMPV6) )
printf("\tQuoted ICMP checksum: %d\n", sport);
if (sum) printf("\tCksum of probe dst: %d\n", sum);
}
char *
ICMP::getMPLS() {
static char *mpls_label_string = (char *) calloc(1, PKTSIZE);
static char *label = (char *) calloc(1, PKTSIZE);
memset(mpls_label_string, 0, PKTSIZE);
memset(label, 0, PKTSIZE);
mpls_label_t *head = mpls_stack;
if (not head)
snprintf(mpls_label_string, PKTSIZE, "0");
while (head) {
//printf("**** LABEL: %d TTL: %d\n", head->label, head->ttl);
if (head->next)
snprintf(label, PKTSIZE, "%d:%d,", head->label, head->ttl);
else
snprintf(label, PKTSIZE, "%d:%d", head->label, head->ttl);
strcat(mpls_label_string, label);
head = head->next;
}
return mpls_label_string;
}
void
ICMP4::print() {
char src[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &ip_src, src, INET_ADDRSTRLEN);
char dst[INET_ADDRSTRLEN] = "no-quote";
uint16_t sum = 0;
if (quote) {
inet_ntop(AF_INET, &(quote->ip_dst), dst, INET_ADDRSTRLEN);
sum = in_cksum((unsigned short *)&(quote->ip_dst), 4);
}
if (verbosity > HIGH) {
printf(">> ICMP response:\n");
ICMP::print(src, dst, sum);
if (mpls_stack)
printf("\t MPLS: [%s]\n", getMPLS());
} else if (verbosity > LOW) {
ICMP::printterse(src);
}
}
void
ICMP6::print() {
char src[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &ip_src, src, INET6_ADDRSTRLEN);
char dst[INET6_ADDRSTRLEN] = "no-quote";
uint16_t sum = 0;
if (quote != NULL) {
inet_ntop(AF_INET6, &(quote->ip6_dst), dst, INET6_ADDRSTRLEN);
sum = in_cksum((unsigned short *)&(quote->ip6_dst), 16);
}
if (verbosity > HIGH) {
printf(">> ICMP6 response:\n");
ICMP::print(src, dst, sum);
} else if (verbosity > LOW) {
ICMP::printterse(src);
}
}
/* trgt, sec, usec, type, code, ttl, hop, rtt, ipid, psize, rsize, rttl, rtos */
void ICMP::write(FILE ** out, uint32_t count, char *src, char *target) {
if (*out == NULL)
return;
fprintf(*out, "%s %lu %ld %d %d ",
target, tv.tv_sec, (long) tv.tv_usec, type, code);
fprintf(*out, "%d %s %d %u ",
ttl, src, rtt, ipid);
fprintf(*out, "%d %d %d %d ",
probesize, replysize, replyttl, replytos);
fprintf(*out, "%s ", getMPLS());
fprintf(*out, "%d\n", count);
}
void ICMP4::write(FILE ** out, uint32_t count) {
if ((sport == 0) and (dport == 0))
return;
char src[INET_ADDRSTRLEN];
char target[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &ip_src, src, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &(quote->ip_dst), target, INET_ADDRSTRLEN);
ICMP::write(out, count, src, target);
}
void ICMP6::write(FILE ** out, uint32_t count) {
char src[INET6_ADDRSTRLEN];
char target[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &ip_src, src, INET6_ADDRSTRLEN);
if (((type == ICMP6_TIME_EXCEEDED) and (code == ICMP6_TIME_EXCEED_TRANSIT)) or
(type == ICMP6_DST_UNREACH)) {
inet_ntop(AF_INET6, &(quote->ip6_dst.s6_addr), target, INET6_ADDRSTRLEN);
}
/* In the case of an ECHO REPLY, the quote does not contain the invoking
* packet, so we rely on the target as encoded in the yarrp payload */
else if (type == ICMP6_ECHO_REPLY) {
inet_ntop(AF_INET6, yarrp_target, target, INET6_ADDRSTRLEN);
}
/* If we don't know what else to do, assume that source of the packet
* was the target */
else {
inet_ntop(AF_INET6, &ip_src, target, INET6_ADDRSTRLEN);
}
ICMP::write(out, count, src, target);
}
struct in6_addr ICMP6::quoteDst6() {
if ((type == ICMP6_TIME_EXCEEDED) and (code == ICMP6_TIME_EXCEED_TRANSIT)) {
return quote->ip6_dst;
}
struct in6_addr a;
memset(&a, 0, sizeof(struct in6_addr));
return a;
}