643a3e07da
PR: ports/67327 Submitted by: Paul Chvostek
434 lines
11 KiB
C
434 lines
11 KiB
C
/* fakeroute (c) 1996 Julian Assange <proff at iq dot org>
|
|
* All Rights Reserved
|
|
*
|
|
* config file ("hops" by default) contains tuples like thus:
|
|
|
|
Dest IP Hop Fake router ms latency
|
|
203.4.184.222 0 204.70.10.250 5
|
|
203.4.184.222 1 204.70.10.250 10
|
|
203.4.184.222 2 198.32.136.88 15
|
|
203.4.184.222 3 137.209.200.202 20
|
|
203.4.184.222 4 137.209.60.1 25
|
|
203.4.184.222 5 198.26.127.26 30
|
|
203.4.184.222 -1 203.4.184.222 35
|
|
203.4.184.217 0 204.70.10.250 5
|
|
203.4.184.217 1 204.70.10.250 10
|
|
203.4.184.217 2 198.32.136.88 15
|
|
203.4.184.217 3 137.209.200.202 20
|
|
203.4.184.217 4 137.209.60.1 25
|
|
203.4.184.217 5 198.26.127.26 30
|
|
203.4.184.217 -1 203.4.184.222 35
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/udp.h>
|
|
#include <netinet/ip_icmp.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <pcap.h>
|
|
|
|
#define bool int
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
#define ENDIAN_RAW_BUG 1
|
|
|
|
typedef u_short port_t;
|
|
|
|
pcap_t *pd;
|
|
struct bpf_program fcode;
|
|
u_char *pcap_userdata;
|
|
struct in_addr localnet, netmask;
|
|
char ebuf[PCAP_ERRBUF_SIZE];
|
|
int raw;
|
|
int a_max_ttl = 1;
|
|
int a_timeout = 300;
|
|
int a_verbose = 0;
|
|
|
|
void ether_if_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p);
|
|
void ppp_if_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p);
|
|
void analyze_udp(struct ip *ip);
|
|
|
|
struct hop
|
|
{
|
|
struct hop *next;
|
|
struct in_addr dst, hop;
|
|
int latency; /* delay */
|
|
int ttl;
|
|
};
|
|
struct hop *hops;
|
|
|
|
struct udp_state
|
|
{
|
|
struct udp_state *next;
|
|
struct in_addr src, dst;
|
|
port_t sport;
|
|
time_t last_packet;
|
|
};
|
|
|
|
struct udp_state *state;
|
|
struct udp_state *queue;
|
|
|
|
void
|
|
pexit(char *err)
|
|
{
|
|
perror(err);
|
|
exit(1);
|
|
}
|
|
|
|
void
|
|
eexit(char *err)
|
|
{
|
|
fprintf(stderr, "fatal error: %s\n", err);
|
|
exit(1);
|
|
}
|
|
|
|
void *
|
|
xmalloc(int n)
|
|
{
|
|
void *p=malloc(n);
|
|
if (!p)
|
|
pexit("malloc");
|
|
return p;
|
|
}
|
|
|
|
u_short
|
|
fast_icmp_cksum(struct icmp *icmp) /* well, for C anyway.. */
|
|
{
|
|
register u_short *u = (u_short *)icmp;
|
|
register int sum = 0;
|
|
|
|
sum = *u++;
|
|
u++; /* skip checksum */
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u++;
|
|
sum += *u;
|
|
sum = (sum >> 16) + (sum & 0xffff); /* fold accumulated carries */
|
|
sum += sum>>16;
|
|
return (u_short) ~sum;
|
|
}
|
|
|
|
struct printer
|
|
{
|
|
pcap_handler f;
|
|
int type;
|
|
};
|
|
|
|
/* XXX needed if using old bpf.h */
|
|
#ifndef DLT_ATM_RFC1483
|
|
#define DLT_ATM_RFC1483 11
|
|
#endif
|
|
|
|
static struct printer printers[] =
|
|
{
|
|
{ ether_if_print, DLT_EN10MB },
|
|
{ ether_if_print, DLT_IEEE802 },
|
|
/*
|
|
{ sl_if_print, DLT_SLIP },
|
|
*/
|
|
{ ppp_if_print, DLT_PPP },
|
|
/*
|
|
{ fddi_if_print, DLT_FDDI },
|
|
{ null_if_print, DLT_NULL },
|
|
{ atm_if_print, DLT_ATM_RFC1483 },
|
|
*/
|
|
{ NULL, 0 },
|
|
};
|
|
|
|
static pcap_handler
|
|
lookup_printer(int type)
|
|
{
|
|
struct printer *p;
|
|
|
|
for (p = printers; p->f; ++p)
|
|
if (type == p->type)
|
|
return p->f;
|
|
|
|
fprintf(stderr, "unknown data link type 0x%x", type);
|
|
exit(1);
|
|
/* NOTREACHED */
|
|
}
|
|
|
|
void
|
|
open_pcap(char *dev, bool f_promisc, char *filt, int usec)
|
|
{
|
|
if (!dev)
|
|
{
|
|
dev = pcap_lookupdev(ebuf);
|
|
if (!dev)
|
|
eexit(ebuf);
|
|
}
|
|
pd = pcap_open_live(dev, 96, f_promisc, usec, ebuf);
|
|
if (!pd)
|
|
eexit(ebuf);
|
|
if (pcap_lookupnet(dev, &localnet.s_addr, &netmask.s_addr, ebuf) < 0)
|
|
eexit(ebuf);
|
|
if (pcap_compile(pd, &fcode, filt, 1, (u_long)netmask.s_addr) < 0)
|
|
eexit(pcap_geterr(pd));
|
|
if (pcap_setfilter(pd, &fcode) < 0)
|
|
eexit(pcap_geterr(pd));
|
|
}
|
|
|
|
void
|
|
ether_if_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
|
|
{
|
|
p += 14;
|
|
analyze_udp((struct ip *)p);
|
|
}
|
|
|
|
void
|
|
ppp_if_print(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
|
|
{
|
|
p += 4;
|
|
analyze_udp((struct ip *)p);
|
|
}
|
|
|
|
|
|
struct hop *
|
|
find_hop(struct in_addr dst, int ttl)
|
|
{
|
|
struct hop *p, *ret=NULL;
|
|
for (p = hops; p; p=p->next)
|
|
{
|
|
if (p->dst.s_addr == dst.s_addr)
|
|
{
|
|
if (p->ttl == ttl)
|
|
return p;
|
|
if (p->ttl == -1)
|
|
ret = p;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
icmp_reply(struct ip *iip, struct hop *hop)
|
|
{
|
|
char buf[sizeof(struct ip) + sizeof(struct icmp) + 8];
|
|
struct ip *ip =(struct ip *)buf;
|
|
struct icmp *i =(struct icmp *)(buf+sizeof(struct ip));
|
|
static u_short ip_id;
|
|
bool f_dest = (hop->ttl == -1);
|
|
struct sockaddr_in in;
|
|
int len = sizeof (struct ip) + sizeof (struct icmp) + 8; /* don't bother with the options */
|
|
memset(&in, 0, sizeof in);
|
|
in.sin_family = AF_INET;
|
|
ip->ip_hl = sizeof (struct ip) / 4;
|
|
ip->ip_v = 4;
|
|
ip->ip_tos = 0;
|
|
ip->ip_len = ENDIAN_RAW_BUG? len: htons(len);
|
|
ip->ip_id = htons(ip_id++);
|
|
ip->ip_off = 0;
|
|
ip->ip_ttl = 255;
|
|
ip->ip_p = IPPROTO_ICMP;
|
|
ip->ip_dst = in.sin_addr = iip->ip_src;
|
|
ip->ip_src = hop->hop;
|
|
ip->ip_sum = 0; /* let the os calculate it */
|
|
i->icmp_type = f_dest? ICMP_UNREACH: ICMP_TIMXCEED;
|
|
i->icmp_code = f_dest? ICMP_UNREACH_PORT: ICMP_TIMXCEED_INTRANS;
|
|
memcpy(&i->icmp_ip, iip, sizeof (struct ip)+8);
|
|
i->icmp_cksum = fast_icmp_cksum(i);
|
|
if (a_verbose>0)
|
|
{
|
|
printf("rotorouter: sending %s %s",
|
|
f_dest? "ICMP_UNREACH_PORT": "ICMP_TIMXCEED",
|
|
inet_ntoa(ip->ip_src));
|
|
printf(" -> %s for ttl %d\n",
|
|
inet_ntoa(ip->ip_dst),
|
|
iip->ip_ttl);
|
|
}
|
|
usleep(hop->latency);
|
|
if (sendto (raw, ip, len, 0, (struct sockaddr *)&in, sizeof in)!=len)
|
|
pexit("sendto");
|
|
}
|
|
|
|
void
|
|
analyze_udp(struct ip *ip)
|
|
{
|
|
struct udphdr *u;
|
|
struct hop *hop;
|
|
struct udp_state *p;
|
|
static time_t lastclean;
|
|
time_t t;
|
|
u = (struct udphdr *)((char *)ip+ip->ip_hl*4);
|
|
hop = find_hop(ip->ip_dst, ip->ip_ttl);
|
|
if (!hop)
|
|
return;
|
|
for (p = state; p; p = p->next)
|
|
{
|
|
if (p->src.s_addr == ip->ip_src.s_addr &&
|
|
p->sport == u->uh_sport &&
|
|
p->dst.s_addr == ip->ip_dst.s_addr)
|
|
goto found;
|
|
}
|
|
if (ip->ip_ttl > a_max_ttl)
|
|
return;
|
|
p = xmalloc(sizeof *p);
|
|
p->next = state;
|
|
p->src = ip->ip_src;
|
|
p->dst = ip->ip_dst;
|
|
p->sport = u->uh_sport;
|
|
state = p;
|
|
found:
|
|
p->last_packet = t = time(NULL);
|
|
icmp_reply(ip, hop);
|
|
if (t > lastclean+10)
|
|
{
|
|
struct udp_state *prev = NULL;
|
|
for (p=state; p;)
|
|
{
|
|
if (t > p->last_packet + a_timeout)
|
|
{
|
|
struct udp_state *p2 = p->next;
|
|
if (prev)
|
|
prev->next = p->next;
|
|
else
|
|
state = NULL;
|
|
free (p);
|
|
p = p2;
|
|
} else
|
|
{
|
|
prev = p;
|
|
p = p->next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
open_raw()
|
|
{
|
|
int yes = 1;
|
|
raw = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
|
|
if (raw<0)
|
|
pexit("socket");
|
|
if (setsockopt(raw, IPPROTO_IP, IP_HDRINCL, (char *)&yes, sizeof(yes)) < 0)
|
|
pexit("IP_HDRINCL");
|
|
}
|
|
|
|
void
|
|
populate_hops(char *fn)
|
|
{
|
|
FILE *fh;
|
|
char host[32];
|
|
char hop[32];
|
|
struct hop *p;
|
|
int latency;
|
|
int ttl;
|
|
int rules;
|
|
fh = fopen(fn, "r");
|
|
if (!fh)
|
|
pexit(fn);
|
|
for (rules = 0; fscanf(fh, "%16s %d %16s %d%*[^\r\n]", host, &ttl, hop, &latency) == 4; rules++)
|
|
{
|
|
struct in_addr hostaddr, hopaddr;
|
|
hostaddr.s_addr = inet_addr(host);
|
|
if (hostaddr.s_addr == 0xffffffff)
|
|
{
|
|
fprintf(stderr, "bad address: %s\n", host);
|
|
exit(1);
|
|
}
|
|
hopaddr.s_addr = inet_addr(hop);
|
|
if (hopaddr.s_addr == 0xffffffff)
|
|
{
|
|
fprintf(stderr, "bad address: %s\n", hop);
|
|
exit(1);
|
|
}
|
|
p = malloc(sizeof *p);
|
|
p->dst = hostaddr;
|
|
p->hop = hopaddr;
|
|
p->ttl = ttl;
|
|
p->latency = latency;
|
|
p->next = hops;
|
|
hops = p;
|
|
|
|
}
|
|
fprintf(stderr, "%d rotorouter rules loaded\n", rules);
|
|
}
|
|
|
|
void
|
|
usage(char *av0)
|
|
{
|
|
exit(1);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int c;
|
|
pcap_handler printer;
|
|
char *a_iface = NULL;
|
|
char *a_filter = "udp";
|
|
char *a_hops = "hops";
|
|
int a_usec = 50;
|
|
bool f_promisc = TRUE;
|
|
while ((c=getopt(argc, argv, "i:ph:u:n:v"))!=-1)
|
|
{
|
|
switch(c)
|
|
{
|
|
case 'i':
|
|
a_iface = optarg;
|
|
break;
|
|
case 'p':
|
|
f_promisc = FALSE;
|
|
break;
|
|
case 'h':
|
|
a_hops = optarg;
|
|
break;
|
|
case 'u':
|
|
a_usec = atoi(optarg);
|
|
break;
|
|
case 't':
|
|
a_max_ttl = atoi(optarg);
|
|
break;
|
|
case 'n':
|
|
a_timeout = atoi(optarg);
|
|
break;
|
|
case 'v':
|
|
a_verbose++;
|
|
break;
|
|
default:
|
|
usage(argv[0]);
|
|
/* NOT REACHED */
|
|
}
|
|
}
|
|
populate_hops(a_hops);
|
|
open_pcap(a_iface, f_promisc, a_filter, a_usec);
|
|
printer = lookup_printer(pcap_datalink(pd));
|
|
open_raw();
|
|
if (pcap_loop(pd, 0, printer, NULL) < 0)
|
|
{
|
|
fprintf(stderr, "%s: pcap_loop: %s\n",
|
|
argv[0], pcap_geterr(pd));
|
|
exit(1);
|
|
}
|
|
pcap_close(pd);
|
|
exit(0);
|
|
}
|