JackSkellington
Technical User
Currently away from a Linux box could someone plase try and compile this for me.
[/code] /*
* Proof Of Concept : counting host behind a NAT using timestamp
* To compile this file, you will need the libpcap
* Copyright Elie Bursztein (lupin@zonart.net)
* Successfully compiled on FreeBSD 5.X and Linux 2.6.X
*
* $gcc natcount.c -o natcount -I/usr/local/include -L/usr/local/lib
* -lpcap
*/
#define __USE_BSD 1
#include <sys/time.h>
#include <time.h>
#include <netinet/in.h>
#include <net/ethernet.h>
#ifdef __FreeBSD__
# include <netinet/in_systm.h>
#endif /* __FreeBSD__ */
#ifdef __linux__
# include <linux/if_ether.h>
#endif /* __linux__ */
#include <netinet/ip.h>
#include <stdlib.h>
#include <string.h>
#include <pcap.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <net/if.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#ifdef __linux__
# define th_off doff
#endif /* __linux__ */
u_int32_t addr = 0;
/* chain lists structures */
typedef struct listes_s {
struct listes_s *next;
void *elt;
} listes_t;
/* Structures for TCP options */
typedef struct { u_int32_t ts, ts_r; } timestamp_t;
typedef struct { timestamp_t *ts; } tcp_opt_t;
/* Structures for datas storage */
typedef struct { u_int32_t from, first_timestamp; struct timeval
first_seen; } machine_t;
typedef struct { u_int32_t host, nat; struct timeval first_seen; }
nat_box_t;
#define TIMESTAMP_ERROR_MARGIN 0.5
#define DELAY 1
/*
* List functions
*/
int add_in_list(listes_t **list, void * elt) {
listes_t *lst;
lst = malloc(sizeof (listes_t));
lst->next = *list;
lst->elt = elt;
*list = lst;
return (1);
}
void show_nated(listes_t *list) {
nat_box_t *nat;
struct in_addr addr;
printf("-- Begin of nated IP list --\n");
while (list)
{
nat = (nat_box_t *) list->elt;
if (nat->nat > 1) {
addr.s_addr = nat->host;
printf("I've guess %i computers sharing the same IP address
(%s)\n", nat->nat, inet_ntoa(addr));
}
list = list->next;
}
printf("-- End of nated IP list --\n");
}
/*
* Function used to get all TCP options
* Simple TCP options parser
*/
int tcp_option_parser(const u_char *options,
tcp_opt_t *parsed,
unsigned int size) {
u_int8_t kind, len, i;
bzero(parsed, sizeof(tcp_opt_t));
i = 0;
kind = *(options + i);
while (kind != 0) /* EO */
{
switch (kind) {
case 1: i++; break; /* NOP byte */
case 2: i += 4; break;
case 3: i += 3; break;
case 4: i += 2; break;
case 5: /* skipping SACK options */
len = (*options + ++i) - 1;
i += len;
break;
case 6: i += 6; break;
case 7: i += 6; break;
case 8:
i += 2;
parsed->ts = (timestamp_t *) (options + i);
i += 8;
return (1);
break;
default:
i++;
}
kind = *(options + i);
}
return (0);
}
/*
* Most interesting function ... Here we can know if a TCP packet is
* coming from someone we already know !
* Algo :
* finc (seconds) = current_packet_time - first_packet_time <- time
* between 2 packets
* ts_inc = inc_table * finc <- our supposed timestamp increment
* between 2 packets
* new_ts = first_timestamp + ts_inc <- new = timestamp we should have
* now !
*
* Now we just have to compare new_ts with current timestamp
* We can authorize an error margin of 0.5%
*
* Our inc_table contain timestamp increment per second for most
* Operating System
*/
int already_seen(machine_t *mach, tcp_opt_t *opt,
struct timeval temps)
{
int inc_table[4] = {2, 10, 100, 1000};
unsigned int new_ts;
float finc, tmp, ts_inc;
int i, diff;
finc = ((temps.tv_sec - mach->first_seen.tv_sec) * 1000000.
+ (temps.tv_usec - mach->first_seen.tv_usec)) / 1000000.;
for (i = 0; i < 4; i++) {
ts_inc = inc_table * finc;
new_ts = ts_inc + mach->first_timestamp;
diff = ntohl(opt->ts->ts) - new_ts;
if (diff == 0) { /* Perfect shoot ! */
return (2);
}
tmp = 100. - (new_ts * 100. / ntohl(opt->ts->ts));
if (tmp < 0.)
tmp *= -1.;
if (tmp <= TIMESTAMP_ERROR_MARGIN) { /* Update timestamp and time */
mach->first_seen = temps;
mach->first_timestamp = ntohl(opt->ts->ts);
return (1);
}
}
return (0);
}
/*
* Simple function to check if an IP address is already in our list
* If not, it's only a new connection
*/
int is_in_list(listes_t *lst, u_int32_t addr) {
machine_t *mach;
while (lst) {
mach = (machine_t *) lst->elt;
if (mach->from == addr)
return (1);
lst = lst->next;
}
return (0);
}
/*
* This function should be call if a packet from an IP address have been
* found,
* is address is already in the list, but doesn't match any timestamp
* value
*/
int update_nat(listes_t *list, u_int32_t addr)
{
nat_box_t *box;
while (list)
{
box = (nat_box_t *) list->elt;
if (box->host == addr)
{
box->nat++;
return (1);
}
list = list->next;
}
return (0);
}
int check_host(listes_t **list, listes_t **nat, u_int32_t
from,
tcp_opt_t *opt, struct timeval temps) {
listes_t *lst;
machine_t *mach;
int found, zaped;
found = zaped = 0;
lst = *list;
while (lst && !(found)) {
mach = (machine_t *) lst->elt;
if (mach->from == from) {
if ( temps.tv_sec - mach->first_seen.tv_sec > DELAY ) {
found = already_seen(mach, opt, temps);
} else zaped = 1;
}
lst = lst->next;
}
if (!(zaped) && !(found)) {
mach = malloc(sizeof (machine_t));
mach->from = from;
mach->first_seen = temps;
mach->first_timestamp = ntohl(opt->ts->ts);
add_in_list(list, mach);
update_nat(*nat, from);
show_nated(*nat);
return (1);
}
return (0);
}
void callback_sniffer(u_char *useless,
const struct pcap_pkthdr* pkthdr,
const u_char *packet)
{
static listes_t *list_machines = 0;
static listes_t *list_nat = 0;
const struct ip *ip_h;
const struct tcphdr *tcp_h;
tcp_opt_t tcp_opt;
machine_t *mach;
nat_box_t *nat;
struct in_addr my_addr;
ip_h = (struct ip *) (packet + sizeof(struct ether_header));
if (ip_h->ip_p == IPPROTO_TCP)
{
tcp_h = (struct tcphdr *) (packet + sizeof(struct ether_header) +
sizeof(struct ip));
if (tcp_h->th_off * 4 > 20) {
if (tcp_option_parser((u_char *) (packet + sizeof(struct
ether_header)
+ sizeof(struct ip) +
sizeof(struct tcphdr)),
&tcp_opt, tcp_h->th_off * 4 - 20))
{
if (is_in_list(list_machines, (ip_h->ip_src).s_addr)) {
check_host(&list_machines, &list_nat, (u_int32_t)
(ip_h->ip_src).s_addr, &tcp_opt, pkthdr->ts);
} else {
if (ntohl(tcp_opt.ts->ts) != 0)
{
addr = (ip_h->ip_src).s_addr;
my_addr.s_addr = addr;
mach = malloc(sizeof (machine_t));
mach->from = (ip_h->ip_src).s_addr;
mach->first_seen = pkthdr->ts;
mach->first_timestamp = ntohl(tcp_opt.ts->ts);
nat = malloc(sizeof (nat_box_t));
nat->host = (u_int32_t) (ip_h->ip_src).s_addr;
nat->nat = 1;
nat->first_seen = mach->first_seen;
add_in_list(&list_machines, mach);
add_in_list(&list_nat, nat);
}
}
}
}
}
}
int main(int ac, char *argv[])
{
pcap_t *sniff;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
char *device;
bpf_u_int32 maskp, netp;
struct in_addr my_ip_addr;
char filter[250];
if (getuid() != 0) {
printf("You must be root to use this tool.\n");
exit (2);
}
if (--ac != 1)
{
printf("Usage: ./natcount xl0\n");
return (1);
}
device = (++argv)[0];
pcap_lookupnet(device, &netp, &maskp, errbuf);
my_ip_addr.s_addr = (u_int32_t) netp;
printf("Using interface %s IP : %s\n", device, inet_ntoa(my_ip_addr));
if ((sniff = pcap_open_live(device, BUFSIZ, 1, 1000, errbuf)) == NULL)
{
printf("ERR: %s\n", errbuf);
exit(1);
}
bzero(filter, 250);
snprintf(filter, 250, "not src net %s", inet_ntoa(my_ip_addr));
if(pcap_compile(sniff,&fp, filter, 0, netp) == -1) {
fprintf(stderr,"Error calling pcap_compile\n");
exit(1);
}
if(pcap_setfilter(sniff,&fp) == -1) {
fprintf(stderr,"Error setting filter\n");
exit(1);
}
pcap_loop(sniff, -1, callback_sniffer, NULL);
return (0);
}
[/code] /*
* Proof Of Concept : counting host behind a NAT using timestamp
* To compile this file, you will need the libpcap
* Copyright Elie Bursztein (lupin@zonart.net)
* Successfully compiled on FreeBSD 5.X and Linux 2.6.X
*
* $gcc natcount.c -o natcount -I/usr/local/include -L/usr/local/lib
* -lpcap
*/
#define __USE_BSD 1
#include <sys/time.h>
#include <time.h>
#include <netinet/in.h>
#include <net/ethernet.h>
#ifdef __FreeBSD__
# include <netinet/in_systm.h>
#endif /* __FreeBSD__ */
#ifdef __linux__
# include <linux/if_ether.h>
#endif /* __linux__ */
#include <netinet/ip.h>
#include <stdlib.h>
#include <string.h>
#include <pcap.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <net/if.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#ifdef __linux__
# define th_off doff
#endif /* __linux__ */
u_int32_t addr = 0;
/* chain lists structures */
typedef struct listes_s {
struct listes_s *next;
void *elt;
} listes_t;
/* Structures for TCP options */
typedef struct { u_int32_t ts, ts_r; } timestamp_t;
typedef struct { timestamp_t *ts; } tcp_opt_t;
/* Structures for datas storage */
typedef struct { u_int32_t from, first_timestamp; struct timeval
first_seen; } machine_t;
typedef struct { u_int32_t host, nat; struct timeval first_seen; }
nat_box_t;
#define TIMESTAMP_ERROR_MARGIN 0.5
#define DELAY 1
/*
* List functions
*/
int add_in_list(listes_t **list, void * elt) {
listes_t *lst;
lst = malloc(sizeof (listes_t));
lst->next = *list;
lst->elt = elt;
*list = lst;
return (1);
}
void show_nated(listes_t *list) {
nat_box_t *nat;
struct in_addr addr;
printf("-- Begin of nated IP list --\n");
while (list)
{
nat = (nat_box_t *) list->elt;
if (nat->nat > 1) {
addr.s_addr = nat->host;
printf("I've guess %i computers sharing the same IP address
(%s)\n", nat->nat, inet_ntoa(addr));
}
list = list->next;
}
printf("-- End of nated IP list --\n");
}
/*
* Function used to get all TCP options
* Simple TCP options parser
*/
int tcp_option_parser(const u_char *options,
tcp_opt_t *parsed,
unsigned int size) {
u_int8_t kind, len, i;
bzero(parsed, sizeof(tcp_opt_t));
i = 0;
kind = *(options + i);
while (kind != 0) /* EO */
{
switch (kind) {
case 1: i++; break; /* NOP byte */
case 2: i += 4; break;
case 3: i += 3; break;
case 4: i += 2; break;
case 5: /* skipping SACK options */
len = (*options + ++i) - 1;
i += len;
break;
case 6: i += 6; break;
case 7: i += 6; break;
case 8:
i += 2;
parsed->ts = (timestamp_t *) (options + i);
i += 8;
return (1);
break;
default:
i++;
}
kind = *(options + i);
}
return (0);
}
/*
* Most interesting function ... Here we can know if a TCP packet is
* coming from someone we already know !
* Algo :
* finc (seconds) = current_packet_time - first_packet_time <- time
* between 2 packets
* ts_inc = inc_table * finc <- our supposed timestamp increment
* between 2 packets
* new_ts = first_timestamp + ts_inc <- new = timestamp we should have
* now !
*
* Now we just have to compare new_ts with current timestamp
* We can authorize an error margin of 0.5%
*
* Our inc_table contain timestamp increment per second for most
* Operating System
*/
int already_seen(machine_t *mach, tcp_opt_t *opt,
struct timeval temps)
{
int inc_table[4] = {2, 10, 100, 1000};
unsigned int new_ts;
float finc, tmp, ts_inc;
int i, diff;
finc = ((temps.tv_sec - mach->first_seen.tv_sec) * 1000000.
+ (temps.tv_usec - mach->first_seen.tv_usec)) / 1000000.;
for (i = 0; i < 4; i++) {
ts_inc = inc_table * finc;
new_ts = ts_inc + mach->first_timestamp;
diff = ntohl(opt->ts->ts) - new_ts;
if (diff == 0) { /* Perfect shoot ! */
return (2);
}
tmp = 100. - (new_ts * 100. / ntohl(opt->ts->ts));
if (tmp < 0.)
tmp *= -1.;
if (tmp <= TIMESTAMP_ERROR_MARGIN) { /* Update timestamp and time */
mach->first_seen = temps;
mach->first_timestamp = ntohl(opt->ts->ts);
return (1);
}
}
return (0);
}
/*
* Simple function to check if an IP address is already in our list
* If not, it's only a new connection
*/
int is_in_list(listes_t *lst, u_int32_t addr) {
machine_t *mach;
while (lst) {
mach = (machine_t *) lst->elt;
if (mach->from == addr)
return (1);
lst = lst->next;
}
return (0);
}
/*
* This function should be call if a packet from an IP address have been
* found,
* is address is already in the list, but doesn't match any timestamp
* value
*/
int update_nat(listes_t *list, u_int32_t addr)
{
nat_box_t *box;
while (list)
{
box = (nat_box_t *) list->elt;
if (box->host == addr)
{
box->nat++;
return (1);
}
list = list->next;
}
return (0);
}
int check_host(listes_t **list, listes_t **nat, u_int32_t
from,
tcp_opt_t *opt, struct timeval temps) {
listes_t *lst;
machine_t *mach;
int found, zaped;
found = zaped = 0;
lst = *list;
while (lst && !(found)) {
mach = (machine_t *) lst->elt;
if (mach->from == from) {
if ( temps.tv_sec - mach->first_seen.tv_sec > DELAY ) {
found = already_seen(mach, opt, temps);
} else zaped = 1;
}
lst = lst->next;
}
if (!(zaped) && !(found)) {
mach = malloc(sizeof (machine_t));
mach->from = from;
mach->first_seen = temps;
mach->first_timestamp = ntohl(opt->ts->ts);
add_in_list(list, mach);
update_nat(*nat, from);
show_nated(*nat);
return (1);
}
return (0);
}
void callback_sniffer(u_char *useless,
const struct pcap_pkthdr* pkthdr,
const u_char *packet)
{
static listes_t *list_machines = 0;
static listes_t *list_nat = 0;
const struct ip *ip_h;
const struct tcphdr *tcp_h;
tcp_opt_t tcp_opt;
machine_t *mach;
nat_box_t *nat;
struct in_addr my_addr;
ip_h = (struct ip *) (packet + sizeof(struct ether_header));
if (ip_h->ip_p == IPPROTO_TCP)
{
tcp_h = (struct tcphdr *) (packet + sizeof(struct ether_header) +
sizeof(struct ip));
if (tcp_h->th_off * 4 > 20) {
if (tcp_option_parser((u_char *) (packet + sizeof(struct
ether_header)
+ sizeof(struct ip) +
sizeof(struct tcphdr)),
&tcp_opt, tcp_h->th_off * 4 - 20))
{
if (is_in_list(list_machines, (ip_h->ip_src).s_addr)) {
check_host(&list_machines, &list_nat, (u_int32_t)
(ip_h->ip_src).s_addr, &tcp_opt, pkthdr->ts);
} else {
if (ntohl(tcp_opt.ts->ts) != 0)
{
addr = (ip_h->ip_src).s_addr;
my_addr.s_addr = addr;
mach = malloc(sizeof (machine_t));
mach->from = (ip_h->ip_src).s_addr;
mach->first_seen = pkthdr->ts;
mach->first_timestamp = ntohl(tcp_opt.ts->ts);
nat = malloc(sizeof (nat_box_t));
nat->host = (u_int32_t) (ip_h->ip_src).s_addr;
nat->nat = 1;
nat->first_seen = mach->first_seen;
add_in_list(&list_machines, mach);
add_in_list(&list_nat, nat);
}
}
}
}
}
}
int main(int ac, char *argv[])
{
pcap_t *sniff;
char errbuf[PCAP_ERRBUF_SIZE];
struct bpf_program fp;
char *device;
bpf_u_int32 maskp, netp;
struct in_addr my_ip_addr;
char filter[250];
if (getuid() != 0) {
printf("You must be root to use this tool.\n");
exit (2);
}
if (--ac != 1)
{
printf("Usage: ./natcount xl0\n");
return (1);
}
device = (++argv)[0];
pcap_lookupnet(device, &netp, &maskp, errbuf);
my_ip_addr.s_addr = (u_int32_t) netp;
printf("Using interface %s IP : %s\n", device, inet_ntoa(my_ip_addr));
if ((sniff = pcap_open_live(device, BUFSIZ, 1, 1000, errbuf)) == NULL)
{
printf("ERR: %s\n", errbuf);
exit(1);
}
bzero(filter, 250);
snprintf(filter, 250, "not src net %s", inet_ntoa(my_ip_addr));
if(pcap_compile(sniff,&fp, filter, 0, netp) == -1) {
fprintf(stderr,"Error calling pcap_compile\n");
exit(1);
}
if(pcap_setfilter(sniff,&fp) == -1) {
fprintf(stderr,"Error setting filter\n");
exit(1);
}
pcap_loop(sniff, -1, callback_sniffer, NULL);
return (0);
}
Code: