#include "bouncer.h"
/* CRC
* Adopted from <a href="http://www.netfor2.com/ipsum.htm">http://www.netfor2.com/ipsum.htm</a>
*/
typedef unsigned short u16;
typedef unsigned long u32;
u16 ip_sum_calc(u16 len_ip_header, u16 buff[]) {
u16 word16;
u32 sum =0;
u16 i;
// make 16 bit words out of every two adjacent 8 bit words in the packet
// and add them up
for (i = 0; i < len_ip_header; i = i + 2) {
word16 = ((buff[i] << 8)&0xFF00)+(buff[i + 1]&0xFF);
sum = sum + (u32) word16;
}
// take only 16 bits out of the 32 bit sum and add up the carries
while (sum >> 16)
sum = (sum & 0xFFFF)+(sum >> 16);
// one'scomplement the result
sum = ~sum;
return ((u16) sum);
}
void process_pkt(u_char *args, struct pcap_pkthdr *header,
u_char *packet) {
if(header->caplen < header->len){
fprintf(stderr, "ERROR: Packet data not captured completely\n");
return;
}
/* Main function to perform the bouncing */
u_char testmode = *args;
char *serv_addr = (char *) (args + 1);
u_int32_t dst_addr =inet_addr(serv_addr);
//pcap_dumper_t *dumper = NULL;
u_char *dumper = NULL;
memcpy(&dumper, args + 2 + strlen(serv_addr), sizeof (pcap_dumper_t *));
struct request ** prequests = NULL;
memcpy(&prequests, args + 2 + strlen(serv_addr) + sizeof (pcap_dumper_t *), sizeof (struct request**));
//struct request *requests = *prequests;
/* Typecasting packet*/
struct sniff_ethernet *ethernet; /* The ethernet header */
struct sniff_ip *ip; /* The IP header */
struct sniff_icmp *icmp; /* The ICMP header */
char *padding; /* Packet padding */
u_int size_ip;
ethernet = (struct sniff_ethernet*) (packet);
ip = (struct sniff_ip*) (packet + SIZE_ETHERNET);
size_ip = IP_HL(ip)*4;
u_int32_t srcip = ntohl(ip->ip_src.s_addr);
if (size_ip < 20 || size_ip > header->len - SIZE_ETHERNET) {
fprintf(stderr, "ERROR: Invalid IP header length: %ubytes\n", size_ip);
if (testmode == 1) {
header->caplen = 0;
header->len = 0;
pcap_dump(dumper, header, packet);
}
return;
}
icmp = (struct sniff_icmp*) (packet + SIZE_ETHERNET + size_ip);
padding = (u_char *) (packet + SIZE_ETHERNET + size_ip + SIZE_ICMP);
/* Validate the packet */
/* Validate IP header */
/* Check IP version */
u_char v = ip->ip_vhl>> 4;
if(v != 4){
fprintf(stderr, "ERROR: Wrong IP verison %d\n", v);
return;
}
/* Validate CRC */
u16 ipbuf[size_ip];
u16 ipsum = ntohs(ip->ip_sum);
int i;
for (i = 0; i < size_ip; i++) {
if (i == 10 || i == 11)
ipbuf[i] = 0x00;
else
ipbuf[i] = *((u_char *) (packet + SIZE_ETHERNET + i));
}
if (ipsum != ip_sum_calc(size_ip, ipbuf)) {
fprintf(stderr, "ERROR: ip checksum mismatch. Dropping packet from %u.%u.%u.%u\n",
IP_QUAD(srcip));
if (testmode == 1) {
header->caplen = 0;
header->len = 0;
pcap_dump(dumper, header, packet);
}
return;
}
/* Validate TTL */
if (ip->ip_ttl <= 0) {
fprintf(stderr, "ERROR: ip TTL expired. Dropping packet from %u.%u.%u.%u\n",
IP_QUAD(srcip));
if (testmode == 1) {
header->caplen = 0;
header->len = 0;
pcap_dump(dumper, header, packet);
}
return;
}
/* Validate IP source address */
if (srcip == 0x00000000 || srcip >= 0xE0000000
|| (srcip & 0x000000FF) == 0x000000FF || (srcip & 0x000000FF) == 0x00000000) {
fprintf(stderr, "ERROR: ip source address invalid. Dropping packetfrom %u.%u.%u.%u\n",
IP_QUAD(srcip));
if (testmode == 1) {
header->caplen = 0;
header->len = 0;
pcap_dump(dumper, header, packet);
}
return;
}
/* Validate ICMP header */
/* Validate ICMP type and code */
if (!(icmp->icmp_code == 0 && (icmp->icmp_type == 0 || icmp->icmp_type == 8))) {
fprintf(stderr, "ERROR: icmp type orcode unsupported. Dropping packet from %u.%u.%u.%u\n",
IP_QUAD(srcip));
if (testmode == 1) {
header->caplen = 0;
header->len = 0;
pcap_dump(dumper, header, packet);
}
return;
}
/* Validate CRC */
int plen = header->len - SIZE_ETHERNET - size_ip - 8;
u16 icmpbuf[8 + plen];
u16 icmpsum = ntohs(icmp->icmp_sum);
for (i = 0; i < 8;i++) {
if (i == 2 || i == 3)
icmpbuf[i] = 0x00;
else
icmpbuf[i] = *((u_char *) (packet + SIZE_ETHERNET + size_ip + i));
}
for (i = 0; i < plen; i++) {
icmpbuf[i + 8] = *((u_char *) (padding + i));
}
if (icmpsum != ip_sum_calc(8 + plen, icmpbuf)) {
fprintf(stderr, "ERROR: icmp checksum mismatch. Dropping packet from %u.%u.%u.%u\n",
IP_QUAD(srcip));
if(testmode == 1) {
header->caplen = 0;
header->len = 0;
pcap_dump(dumper, header, packet);
}
return;
}
/* Update the packet */
if (icmp->icmp_type == 0) {
/* ICMP echo reply */
ip->ip_src.s_addr = ip->ip_dst.s_addr;
/* Search the linked list for client address */
if (*prequests == NULL) {
fprintf(stderr, "ERROR: process_pkt: nullrequest linked list\n");
return;
} else {
/* Find where the request comes from */
struct request *r;
for (r = *prequests; r != NULL; r = r->next) {
if (r->icmp->icmp_id == icmp->icmp_id && r->icmp->icmp_sequence == icmp->icmp_sequence) {
ip->ip_dst.s_addr = r->ip->ip_src.s_addr;
break;
}
}
if (r == NULL) {
fprintf(stderr, "ERROR: no match echo requests in stack\n");
return;
}
/* Remove the request from the linked list */
struct request *rr = *prequests;
if (rr == r) {
free(rr->icmp);
free(rr->ip);
*prequests = r->next;
free(r);
} else {
while (rr!= r)
rr = rr->next;
free(rr->icmp);
free(rr->ip);
rr->next = r->next;
free(r);
}
}
} else {
/* ICMP echo request */
/* Add new request to linked list */
struct request *r;
r = malloc(sizeof (struct request));
if (r == NULL) {
perror("ERROR: process_pkt:malloc");
return;
}
struct sniff_ip *ipt = malloc(sizeof (struct sniff_ip));
if (ipt == NULL) {
perror("ERROR: process_pkt: malloc");
return;
}
memcpy(ipt, ip, sizeof (struct sniff_ip));
r->ip = ipt;
struct sniff_icmp* icmpt = malloc(sizeof (struct sniff_icmp));
if (icmpt == NULL) {
perror("ERROR: process_pkt: malloc");
return;
}
memcpy(icmpt, icmp, sizeof (struct sniff_icmp));
r->icmp = icmpt;
r->next = *prequests;
*prequests = r;
/* Update destination address */
ip->ip_src.s_addr = ip->ip_dst.s_addr;
ip->ip_dst.s_addr = dst_addr;
}
/* Recaculate CRC */
for (i = 0; i < size_ip; i++) {
if (i == 10 || i == 11)
ipbuf[i] = 0x00;
else
ipbuf[i] = *((u_char *) (packet + SIZE_ETHERNET + i));
}
ip->ip_sum = htons(ip_sum_calc(size_ip, ipbuf));
if (testmode == 0) {
pcap_t *handle = NULL;
char errbuf[PCAP_ERRBUF_SIZE], *device = "tap0";
memset(errbuf, 0, PCAP_ERRBUF_SIZE);
if ((handle = pcap_open_live(device, MAX_PACKET_SIZE, 1, 512, errbuf)) == NULL) {
fprintf(stderr, "ERROR: %s\n", errbuf);
exit(1);
}
/*Send the packet to network */
if (pcap_sendpacket(handle, packet, header->len) != 0) {
perror("ERROR: process_pkt: pcap_sendpacket");
exit(1);
}
pcap_close(handle);
fprintf(stderr, "Bouncer: packet sent\n");
} else {
/* Or put it back on stdout */
int i = 0;
for (i = 0; i < ETHER_ADDR_LEN; i++)
ethernet->ether_shost[i] = 1;
if(icmp->icmp_type == 0) {
for (i = 0; i < ETHER_ADDR_LEN; i++)
ethernet->ether_dhost[i] = 2;
} else if (icmp->icmp_type == 8) {
for (i = 0; i < ETHER_ADDR_LEN; i++)
ethernet->ether_dhost[i] = 3;
}
pcap_dump(dumper, header, packet);
////////////////////////////////////////////////////////////////////////
/*
pcap_t *handle = NULL;
char errbuf[PCAP_ERRBUF_SIZE], *device = "tap0";
memset(errbuf, 0, PCAP_ERRBUF_SIZE);
if ((handle = pcap_open_live(device, MAX_PACKETS_NO, 1, 512, errbuf)) == NULL) {
fprintf(stderr, "ERROR: %s\n", errbuf);
exit(1);
}
if (pcap_sendpacket(handle, packet, header->len) != 0) {
perror("ERROR: process_pkt: pcap_sendpacket");
exit(1);
}
pcap_close(handle);
fprintf(stderr, "Packet sent\n");
*/
////////////////////////////////////////////////////////////////////////
}
return;
}