mirror of
https://github.com/cmand/yarrp.git
synced 2024-05-11 05:55:06 +00:00
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."
381 lines
14 KiB
C++
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;
|
|
}
|