mirror of
https://github.com/cmand/yarrp.git
synced 2024-05-11 05:55:06 +00:00
351 lines
9.2 KiB
C++
351 lines
9.2 KiB
C++
/****************************************************************************
|
|
Program: $Id: net.cpp 14 2014-12-30 02:33:44Z laalt $
|
|
Date: $Date: 2014-12-29 18:33:44 -0800 (Mon, 29 Dec 2014) $
|
|
Description: networking routines
|
|
****************************************************************************/
|
|
#include "yarrp.h"
|
|
|
|
/**
|
|
* Prints a range of data in hex.
|
|
*
|
|
* @param buf Start of the data range
|
|
* @param len Length of the data range
|
|
* @param brk Number of bytes to print per line
|
|
* @param tabs Number of tabs to insert at the beginning of each new line
|
|
*/
|
|
void
|
|
print_binary(const unsigned char *buf, int len, int brk, int tabs) {
|
|
int i, j;
|
|
for (i = 0; i < len; i++) {
|
|
if ((i > 0) && (i % brk == 0)) {
|
|
printf("\n");
|
|
for (j = 0; j < tabs; j++)
|
|
printf("\t");
|
|
}
|
|
printf("%02X ", buf[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/**
|
|
* Prints each header (net, trnspt, data) of a packet in hex.
|
|
*
|
|
* @param packet Start of the buffer containing the packet
|
|
* @param tot_len Total length of the packet buffer
|
|
*/
|
|
void
|
|
print_packet(const unsigned char *packet, int tot_len) {
|
|
struct ip *ip;
|
|
struct tcphdr *tcp;
|
|
int iph_len, tcph_len, hdrs_len;
|
|
|
|
ip = (struct ip *)packet;
|
|
iph_len = ip->ip_hl << 2;
|
|
|
|
tcp = (struct tcphdr *)(packet + iph_len);
|
|
tcph_len = tcp->th_off << 2;
|
|
|
|
hdrs_len = iph_len + tcph_len;
|
|
|
|
printf("\tIP Header:\t");
|
|
print_binary((unsigned char *)ip, iph_len, 10, 3);
|
|
|
|
printf("\tTCP Header:\t");
|
|
print_binary((unsigned char *)tcp, tcph_len, 10, 3);
|
|
|
|
/* only print a data header if the packet has data */
|
|
if (tot_len > hdrs_len) {
|
|
printf("\tData:\t\t");
|
|
print_binary(packet + hdrs_len, tot_len - hdrs_len, 10, 3);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new raw IPv4 socket.
|
|
*
|
|
* @param sin_orig Source port and address family (only used on PlanetLab)
|
|
* @return Descriptor for the newly created socket
|
|
*/
|
|
int
|
|
raw_sock(struct sockaddr_in *sin_orig) {
|
|
int sock, one = 1;
|
|
struct sockaddr_in sin;
|
|
|
|
/* create a new sin without the address */
|
|
memset(&sin, 0, sizeof sin);
|
|
sin.sin_family = sin_orig->sin_family;
|
|
sin.sin_port = sin_orig->sin_port;
|
|
|
|
/* establish raw socket */
|
|
if ((sock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
|
|
fatal("Create socket failed: %s", strerror(errno));
|
|
|
|
if (setsockopt(sock, 0, IP_HDRINCL, &one, sizeof one) < 0)
|
|
warn("setsockopt failed: %s", strerror(errno));
|
|
|
|
return sock;
|
|
}
|
|
|
|
/**
|
|
* Determine our public-facing IPv4 address.
|
|
*
|
|
* @param mei Where to save the address we find
|
|
* @return 1 for success, -1 for failure
|
|
*/
|
|
int
|
|
infer_my_ip(struct sockaddr_in *mei) {
|
|
struct sockaddr_in me, serv;
|
|
socklen_t len = sizeof me;
|
|
int sock, err;
|
|
|
|
memset(&serv, 0, sizeof serv);
|
|
serv.sin_family = AF_INET;
|
|
serv.sin_addr.s_addr = inet_addr("8.8.8.8");
|
|
serv.sin_port = htons(53);
|
|
|
|
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (sock == -1)
|
|
return -1;
|
|
|
|
err = connect(sock, (const struct sockaddr *)&serv, sizeof serv);
|
|
if (err == -1)
|
|
return -1;
|
|
|
|
err = getsockname(sock, (struct sockaddr *)&me, &len);
|
|
if (err == -1)
|
|
return -1;
|
|
|
|
mei->sin_addr = me.sin_addr;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Determine our public-facing IPv6 address.
|
|
*
|
|
* @param mei6 Where to save the address we find
|
|
* @return 1 for success, -1 for failure
|
|
*/
|
|
int
|
|
infer_my_ip6(struct sockaddr_in6 *mei6) {
|
|
struct sockaddr_in6 me6, serv6;
|
|
socklen_t len = sizeof me6;
|
|
int sock6, err;
|
|
|
|
memset(&serv6, 0, sizeof serv6);
|
|
serv6.sin6_family = AF_INET6;
|
|
inet_pton(AF_INET6, "2001:4860:4860::8888",
|
|
&serv6.sin6_addr.s6_addr);
|
|
serv6.sin6_port = htons(53);
|
|
|
|
sock6 = socket(AF_INET6, SOCK_DGRAM, 0);
|
|
if (sock6 == -1)
|
|
return -1;
|
|
|
|
err = connect(sock6, (const struct sockaddr *)&serv6, sizeof serv6);
|
|
if (err == -1)
|
|
return -1;
|
|
|
|
err = getsockname(sock6, (struct sockaddr *)&me6, &len);
|
|
if (err == -1)
|
|
return -1;
|
|
|
|
mei6->sin6_addr = me6.sin6_addr;
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Lookup the given address in DNS.
|
|
*
|
|
* @param url DNS or IPv4 address in string form
|
|
* @param target Where to save the IPv4 address we find
|
|
*/
|
|
void
|
|
resolve_target_ip(char *url, struct sockaddr_in *target) {
|
|
int error;
|
|
struct addrinfo hints, *result;
|
|
|
|
/* hints allows us to tell getaddrinfo what kind of answer we want */
|
|
memset(&hints, 0, sizeof hints);
|
|
hints.ai_family = AF_INET; /* we want IPv4 addresses only */
|
|
|
|
/*
|
|
* we use getaddrinfo() because it can handle either DNS addresses or IPs
|
|
* as strings
|
|
*/
|
|
error = getaddrinfo(url, NULL, &hints, &result);
|
|
if (error)
|
|
fatal("Error in getaddrinfo: %s", gai_strerror(error));
|
|
|
|
/* just grab the first address in the linked list */
|
|
target->sin_addr = ((struct sockaddr_in *)result->ai_addr)->sin_addr;
|
|
|
|
freeaddrinfo(result);
|
|
}
|
|
|
|
/**
|
|
* Compute an IP checksum.
|
|
*
|
|
* @param addr Start of the data range
|
|
* @param len Length of the data range
|
|
* @return 2-byte long IP checksum value
|
|
*/
|
|
unsigned short
|
|
in_cksum(unsigned short *addr, int len) {
|
|
int nleft = len;
|
|
int sum = 0;
|
|
unsigned short *w = addr;
|
|
unsigned short answer = 0;
|
|
|
|
/*
|
|
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
|
|
* sequential 16 bit words to it, and at the end, fold back all the carry
|
|
* bits from the top 16 bits into the lower 16 bits.
|
|
*/
|
|
assert(addr);
|
|
while (nleft > 1) {
|
|
sum += *w++;
|
|
nleft -= 2;
|
|
}
|
|
|
|
/* 4mop up an odd byte, if necessary */
|
|
if (nleft == 1) {
|
|
*(unsigned char *)(&answer) = *(unsigned char *)w;
|
|
sum += answer;
|
|
}
|
|
/* 4add back carry outs from top 16 bits to low 16 bits */
|
|
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
|
|
sum += (sum >> 16); /* add carry */
|
|
answer = ~sum; /* truncate to 16 bits */
|
|
return answer;
|
|
}
|
|
|
|
/*
|
|
* struct ipovly seems to be a BSD only item... Defining it here for now, but
|
|
* there should be a better, more cross-platform way of doing this.
|
|
*/
|
|
#ifndef _BSD
|
|
struct ipovly {
|
|
u_char ih_x1;
|
|
u_char ih_pr;
|
|
u_short ih_len;
|
|
struct in_addr ih_src;
|
|
struct in_addr ih_dst;
|
|
};
|
|
#endif
|
|
|
|
struct ip6ovly {
|
|
struct in6_addr ip6_src;
|
|
struct in6_addr ip6_dst;
|
|
uint32_t ip6_len;
|
|
u_char ip6_zeros[3];
|
|
u_char ip6_pr;
|
|
};
|
|
|
|
/*
|
|
* Checksum routine for UDP and TCP headers.
|
|
*/
|
|
u_short
|
|
p_cksum(struct ip *ip, u_short * data, int len) {
|
|
static struct ipovly ipo;
|
|
u_short sumh, sumd;
|
|
u_long sumt;
|
|
|
|
ipo.ih_pr = ip->ip_p;
|
|
ipo.ih_len = htons(len);
|
|
ipo.ih_src = ip->ip_src;
|
|
ipo.ih_dst = ip->ip_dst;
|
|
|
|
sumh = in_cksum((u_short *) & ipo, sizeof(ipo)); /* pseudo ip hdr cksum */
|
|
sumd = in_cksum((u_short *) data, len); /* payload data cksum */
|
|
sumt = (sumh << 16) | (sumd);
|
|
|
|
return ~in_cksum((u_short *) & sumt, sizeof(sumt));
|
|
}
|
|
|
|
/*
|
|
* IPv6 checksum routine for UDP/TCP/ICMP (Section 8.1 of RFC 2460).
|
|
*/
|
|
u_short
|
|
p_cksum(struct ip6_hdr *ip6, u_short * data, int len) {
|
|
static struct ip6ovly ipo;
|
|
u_short sumh, sumd;
|
|
u_long sumt;
|
|
|
|
ipo.ip6_pr = ip6->ip6_nxt;
|
|
ipo.ip6_len = htons(len);
|
|
ipo.ip6_src = ip6->ip6_src;
|
|
ipo.ip6_dst = ip6->ip6_dst;
|
|
|
|
sumh = in_cksum((u_short *) & ipo, sizeof(ipo)); /* pseudo ip hdr cksum */
|
|
sumd = in_cksum((u_short *) data, len); /* payload data cksum */
|
|
sumt = (sumh << 16) | (sumd);
|
|
|
|
return ~in_cksum((u_short *) & sumt, sizeof(sumt));
|
|
}
|
|
|
|
/*
|
|
* Compute packet payload needed to make UDP checksum correct
|
|
* (Used to ensure Paris-style load balancing
|
|
*/
|
|
unsigned short compute_data(unsigned short start_cksum, unsigned short target_cksum) {
|
|
unsigned short answer = 0x0000;
|
|
/* per RFC, if computed checksum is 0, the value 0xFFFF is transmitted */
|
|
if (target_cksum == 0xFFFF)
|
|
target_cksum = 0x0000;
|
|
/* if the ones' complement of the target checksum is greater than
|
|
* the ones' complement of the starting checksum, use the overflow
|
|
* in the computation of IP/UDP checksum to keep result positive
|
|
*/
|
|
if (~target_cksum > ~start_cksum)
|
|
answer = ~target_cksum - (~start_cksum);
|
|
else
|
|
answer = 0xFFFF - (~start_cksum) + (~target_cksum);
|
|
return answer;
|
|
}
|
|
|
|
/* @@ RB: Gaston v6 code only supports Linux */
|
|
#ifdef _LINUX
|
|
/**
|
|
* Create a new raw IPv6 socket.
|
|
*
|
|
* @param sin_orig Source port and address family
|
|
* @return Descriptor for the newly created socket
|
|
*/
|
|
int
|
|
raw_sock6(struct sockaddr_in6 *sin6_orig) {
|
|
int sock, one = 1;
|
|
struct sockaddr_in6 sin6;
|
|
|
|
/* create a new sin without the address */
|
|
memset(&sin6, 0, sizeof sin6);
|
|
sin6.sin6_family = sin6_orig->sin6_family;
|
|
sin6.sin6_port = sin6_orig->sin6_port;
|
|
|
|
/* establish raw socket */
|
|
if ((sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0)
|
|
fatal("Create socket failed: %s", strerror(errno));
|
|
|
|
return sock;
|
|
}
|
|
#endif
|
|
|
|
/* BPF dev finder/opener */
|
|
int bpfget() {
|
|
char dev[32];
|
|
int fd = -1;
|
|
for (int i=0; i < 255; i++) {
|
|
snprintf(dev, sizeof(dev), "/dev/bpf%u", i);
|
|
//printf("trying to open %s\n", dev);
|
|
fd = open(dev, O_RDWR);
|
|
if (fd > -1) return fd;
|
|
switch (errno) {
|
|
case EBUSY:
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|