wtorek, 2 lipca 2013

Phrack nr 63 - "TCP Timestamp do liczenia hostów za NAT-em"

|=-------------------------=[ 0x03-2 ]=----------------------------------=|
|=---------=[ TCP Timestamp do liczenia hostów za NAT-em ]=---------------=|
|=-----------------------------------------------------------------------=|
|=-------------=[ Elie aka Lupin (lupin@zonart.net) ]=-------------------=|

Tabela zawartości
*=*=*=*=*=*=*=*=*

1.0 Wprowadzenie

2.0 Czas ma nam coś do powiedzenia

- 2.1 Przeszłość
- 2.2 Teraźniejszość
- 2.3 Powrót do początkowej historii timestamp
- 2.4 Powrót do NAT
- 2.5 Omówmy PAT
- 2.6 Czas na zwalczanie

3.0 Historia ma nam coś do powiedzenia

- 3.1 Która klasa?
- 3.2 Więc skąd to się wzięło?
- 3.3 Jak to znaleźć?
- 3.4 Powrót do przyszłości

4.0 Nauka z przełości aka wniosek

A - Uznania

B - Dowód koncepcji


--[ 1.0 Wprowadzenie

Ten artykuł jest o rozwiązaniu jakim jest TCP timestamp. Rozwiązanie to oferuje 
nową możliwość liczenia hostów będących za NAT-em oraz podwyższony fingerprinting
tych hostów. Dogłębniej: ten artykuł próbuje przedstawić nową wizję klasy
znanych dziur w oprogramowaniu mających "projektowany błąd". Ta dziura została
opisana tutaj i jest godna zainteresowania choćby z następujących powodów:
- jest nowa,
- działa na każdej platformie odkąd jest to związane raczej ze specyfikacją
niż implementacją,
- jest to niezła możliwość aby wyjaśnić jak niektóre specyfikacje mogą być
złamane.

Ten artykuł jest zorganizowany następująco: najpierw wyjąśnię co jest złego
w TCP timestamp. Potem opiszę jak to wykorzystać, ograniczenia tego
wykorzystania oraz jak tego uniknąć. W drugiej części pomówię o pochodzeniu
tego błędu i dlaczego to się znowu zdarza. Na koniec przedstawię dowód
koncepcji i pozdrowienia jak zwykle.

--[ 2.0 Czas ma nam coś do powiedzenia

----[ 2.1 Przeszłość

Fingerprinting i detekcja NAT były aktywnymi dziedzinami od bardzo dawna.
Od czasu kiedy czytasz Phrack znasz już starą szkołę TCP/IP Fyodora.

Możesz znać także p0f (passive OS fingerprinting) Michała Zalewskiego. Z
wersją 2. ukończył on wspaniałe narzędzie, przedstawia pomysłowe drogi
na zrozumienie czy hosty używają mechanizmu NAT przez analizę opcji
pakietu TCP. Jeżeli jesteś zainteresowany tym narzędziem (a powienieneś!)
przeczytaj jego artykuł: "Dr. Jekyll had something to Hyde" [5]. 

Faktycznie, opisana tutaj technika jest związana z p0f, coś jak p0f, to
może być całkowicie pasywne.

Żeby skończyć temat detekcji NAT muszę nadmienić że AT&T ukończyło badania
nad liczeniem hostów będących za NAT-em [1]. Ich praca była skupiona na IP
ID, przypuszczali że ta wartość jest inkrementowana w niektórych systemach.
Rzeczywiście, mowili oni głownie o Windows który zwiększa IP ID każdego
pakietu o 256. Odkryte przez Antirez [7]. Nmap [6] użył tego dawno temu
(opcja -sI). 

Teraz wiemy o czym mówimy. Czas wyjaśnić o co chodzi.

----[ 2.2 Teraźniejszość

NAT został zaprojektowany aby stanąć naprzeciw wyczerpaniu puli adresów IP.
Jest także używany aby ukryć wiele hostów będących za pojedyńczym adresem
IP. Opcja TCP timestamp [2] jest błędnie opracowana przez mechanizm
Translacji Adresów Sieciowych czyli Network Adress Translation (NAT) [3]. 

Innymi słowy nawet zeskrobanie tego z p0f nie podrobi opcji timestamp.
Do teraz właściwość NAT-u była bezużyteczna (z punktu widzenia
bezpieczeństwa). Warto zwrócić uwagę na to że opcja timestamp była używana
już poprzednio przez samą siebie do ujawniania informacji. Spójrzmu szybko 
na historię bezpieczeństwa timestamp.

----[ 2.3 Powrót do początkowej historii timestamp

W przeszłości timestamp był używany do obliczania uptime'u komputera
[4]. Każdy kto wypróbował opcję fingerprintingu TCP w Nmapie (-O) był pod
wrażeniem gdy zobaczył coś takiego:

"Uptime 36.027 days (since Tue May 25 11:12:31 2004)".

Oczywiście za tym nie kryje się żadna czarna magia, tylko dwa fakty:
- czas powraca tylko w filmach (przykro mi, chłopcy...),
- wszystkie systemy operacyjne inkrementują timestamp o jeden co
milisekundę. 

Więc jeżeli znasz system operacyjny to wiesz jak często system ten zwiększa
timestamp. Wszystko co musisz zrobić żeby dowiedzieć się ile wynosi uptime 
to zastosować prostą matematyczną formułę: 

timestamp / wartość o którą się zwiększa co sekundę = uptime w sekundach

Możesz zauważyć że ta formuła nie bierze pod uwagę osnowy zaokrąglenia
liczby całkowitej. Teraz znamy dwie rzeczy: aktualny timestamp i liczbę o
którą zwiększa się co sekundę. Skończymy to tylko jeżeli znamy typ systemu 
operacjnego. Zobaczmy jak możemy udoskonalić tą technikę robiąc to bez 
znajomości systemu operacyjnego.

----[ 2.4 Powrót do szkoły 

Pamiętasz? Dawno temu w szkole mówiono o równaniu kierunkowym prostej.
Podstawowym przykładem tego jest:

"y = Ax + B"

gdzie A oznacza monotoniczność funkcji a B punkt początkowy.
Graficzną reprezentacją tego jest linia prosta. Z punktu widzenia timestamp
to może być ekspresowe jak następuje:

timestamp = wartość o którą się zwiększa co sekundę * sekunda + wartość
początkowa

Kiedy wykonujesz aktywny fingerprinting dostajesz timestamp i znasz wartość
o którą się zwiększa co sekundę poprzez odgadnięcie systemu operacyjnego.

Załóżmy że możesz odgadnąć jaki jest system operacyjny. W tym wypadku nie
znasz monotoniczności funkcji i nie możesz odgadnąć uptime'u. Ale jest inna
droga na dowiedzenie się jaka jest monotoniczność systemu operacyjnego.
Musisz zdobyć komputer z podwójnym timestampem. Nazwą jest ts1 i ts2 a
wartość czasu (w sekundach) gdzie jest zapisywany to s1 i s2. 

Mając tamte informacje można bardzo łatwo znaleźć monotoniczność dzięki
następujacemu układowi:

ts1 = A*s1 + B 
ts2 = A*s2 + B 

gdzie rozwiązaniem jest to oto równanie:

ts1 - ts2 = A*(s1 - s2) <=> A = (ts1 - ts2) / (s1 - s2) 

Bezpośrednia aplikacja wykorzystująca ten pomysł może być zaimplementowana
w aktywnym skanerze: dwókrotne żądanie o timestamp w celu weryfikacji czy 
monotoniczność jest taka sama jak przypuszczaliśmy.

To może zostać użyte do złamania jakiegoś anty-fingerprintingowego narzędzia.
Innym zastosowaniem jest użycie tego jako techniki standalone fingerprinting
ale nie będziemy mieć pewności czy ma raz TCP czy ICMP.

Teraz znamy już teorię, wróćmy do NAT-u.

----[ 2.5 Powrót do NAT-u

Utwórzmy połączenie z NAT-em. Odkąd rozwiązanie timestamp nie jest
przepisywane przez NAT, możemy policzyć liczbę hostów za NAT-em używając
następującego algorytmu:

1) dla każdego już odkrytego hosta weryfikowany jest pakiet należący do
tego równania linii prostej. Każdy host ma unikatowe równanie prostej
dopóki dwa hosty są bootowane w tej samej sekundzie.

2) w przeciwnym wypadku należy dodać pakiet do nie pasującego pakietu: nowy
host za NAT-em jest wykrywany.

Rzuć okiem na dowód koncepcji jeżeli chcesz zrobić to bardziej klarownie.
Ten prosty algorytm ma wiele do udoskonalenia. Został przedstawiony jako
prosty i możliwie jak najbardziej przezroczysty. Jak widzisz rozwiązanie
timestamp może zostać użyte do liczenia hostów za NAT-em w sposób niezawodny. 
Poda również wskazówkę dotyczącą klasyfikację systemu operacyjnego.

----[ 2.6 Omówmy PAT

PAT (Port Address Translation - Translacja Adresu Portu) jest używana do
dostarczania usługi na komputerze za NAT-em. 

Pytaniem jest skąd wiem że port jest forwardowany? Timestamp jest raz
jeszcze Twoim przyjacielem. Jeżeli dla dwóch różnych usług monotoniczność 
timestampu jest inna, wtedy PAT i system operacyjny dwóch komputerów są różne. 
Jeżeli timestamp pobrany z dwóch portów nie należy do tej samej prostej, wtedy 
jest to ten sam system ale nie ten sam komputer. 

Inną interesującą funkcją PAT-u jest round robin (od tł.: w wolnym
tłumaczeniu jest to okrągły rudzik). Do teraz nie było żadnej możliwości żeby się
dowiedzieć czy taki mechanizm został użyty. Przez porównywanie różnych
zebranych timestampów możesz ustalić ile hostów jest za pojedyńczym portem.
Dodanie takiej funkcji w aktywnym skanerze mogło być ciekawe.

----[ 2.7 Czas na zwalczanie

Od czasu kiedy gra z tą opcją mogła dać cenne informacje, jest tam
jakieś ograniczenie na tą technikę. Stacje windowsowe właściwie nie używają
rozwiązania timestamp kiedy ustanawiają połączenie [8] chyba że je
aktywujesz. To ograniczenie oddziałuje tylko na pasywną analizę, jeżeli
używasz timestamp do łączenia się z Windowsem to on także będzie tego
używał. Ponadto wiele ściąganych programów aktywuje rozszerzenie TCP w
Windowsie. 

Żeby zakończyć temat chciałbym nadmienić że wydaje się że rozszerzenie TCP
nie istnieje w Windows 9X. 

Jednym z innych problemów jest przerwa czasowa. W trybie pasywnym może być tam
desynchronizacja pomiędzy komputerami spowodowana komputerową
desynchronizacją lub sieciowymi opóźnieniami. W dowodzie koncepcji to
zjawisko może się przytrafić. Żeby to załatwić nie musisz polegać na
komputerowym zegarze ale na timestamp. 

Co możemy zrobić w stosunku do tego? Od czasu kiedy żaden sprzedawca prócz
Microsoftu (1) (dzięki Magnus) nie zadał mi pytania, następujące
obejścia mogą nie być dostępne:

1) Wyłączanie TCP timestamp. To jest gorsze rozwiązanie ponieważ będziemy
tego potrzebowali mimo szybkiej sieci.
2) Przepisanie NAT-u na timestamp i zmienianie The NAT RFC.
3) Zmienianie zawartości RFC na taką że rozwiązanie timestamp musi mieć
przypadkowy przyrost. Modyfikowanie każdej implementacji odbija tą
zmianę. Czyste rozwiązanie utwierdzające tą rzecz ponieważ to nie
polega na zewnętrznym systemie (komputer z NAT-em w tym wypadku).

Więc muszę spróbować być tak kompletnym jak to możliwe w tej technicznej
części. Następna część będzie bardziej "filozoficzna" zaczynając od tego jak
to ma do czynienia z powodem zamiast konsekwencji. 

--[ 3.0 Historia ma nam coś do powiedzenia

W tej części spróbuję skupić się na tym dlaczego mamy taką sytuację i co
możemy z tym zrobić. Nie będę mówił tutaj o rozwiązaniu timestamp samym w
sobie ale o interakcji pomiędzy timestamp a mechanizmem NAT.

----[ 3.1 Która klasa?

Pierwsze pytanie brzmi: co jest bugiem? Ta dziura należy do modelu klasy
błędów. Bardziej precyzyjnie: bug istnieje ponieważ istnieje specyfikacja
protokołu. IP został zaprojektowany jako protokół jeden na jeden: jeden
klient mówi do jednego serwera. NAT narusza tą specyfikację przez
dopuszczenie wielokrotnych odwołań do jednego serwera. Z natury to nadużycie
powoduje wiele problemów dzięki którym tracę to co policzyłem, lecz jest to
dosyć pewne że najbardziej rekurencyjnym problemem jest transfer FTP. Jeżeli
używasz FTP to wiesz o co mi chodzi (inni mogą spojrzeć na netfilter ftp
conntrack).

----[ 3.2 Więc skąd to się wzięło?

Problem FTP jest dobrym przykładem aby wyjaśnić pochodzenie problemu
zachodzącej specyfikacji. FTP został wyspecyfikowany do pracy nad solidnym
połączeniem jeden do jednego (w rzeczywistości TCP). NAT został
zaprojektowany do modyfikacji IP. Więc dzięki zależnościom protokołu to
także zmienia TCP i przeto FTP.

Podczas specyfikacji NAT-u nie było brane pod uwagę że każdy protokół który
polega na IP, może wchodzić w konflikt ze zmodyfikowaną specyfikacją. Jeżeli
chciano powiedzieć prawdę, nawet jeżeli ludzie którzy zaprojektowali mechanizm 
NAT kiedykolwiek potrzebowali gwarancji że każdy protokół który opiera się na 
IP może pracować z NAT-em, nie można było tego zrobić.

Dlaczego? Ponieważ specyfikacje są w RFC i RFC jest po angielsku. Angielski
nie jest dobrym pomysłem na specyfikację rzeczy szczególnie jeżeli masz
wykres zależności dla specyfikacji.

Np. wiele języków programowania ma formalne specyfikcje. Która jest
pełniejszym dowodem rozwiązania. Rozwiązanir tego braku specyfikacji
znajduje się w historii Internetu [9]. W tym czasie pisząc mały tekst była to
dostateczna ilość. Teraz może to być problemem.

----[ 3.3 Jak to znaleźć?

Ważym pytaniem jest, jak mam znaleźć ten bug? Więc, znalazłem ten problem
przez formalizację części RFC TCP i porównanie wyniku tej analizy z
prawdziwymi śladami wykonania. Mój analizator (2) ostrzegł mnie że timestamp
był mniejszy niż jeden poprzedni i jak wiesz czas nie wróci...

Sprawdziłem dlaczego i jak znalazłem ten problem. Interesujące jest tutaj
to że punktem wyjścia do znalezienia tego błędu jest raczej specyfikacja niż
implementacja jak to zwykle można znaleźć przepełnienie bufora dla przykładu. 

----[ 3.4 Powrót do przyszłości

Więc co się od tej porty będzie działo? Ano znajdowanych będzie więcej 
projektowanych błędów ponieważ nie możemy zmienić przeszłości i musimy z tym
żyć. 
Nie jest sensowne mówić że możemy ścierać wszystko to co materiał TCP i 
rozpoczynać nową rzecz od zadrapania. Internet i sieć są po prostu za duże do 
poruszania się w ten sposób. Najprościej mówiąc myślą co sekundę o rozlokowaniu 
IP v6 i przekonasz się o tym. Wszystko co możemy zrobić to być tak ostrożnymi 
jak to możliwe kiedy projektujemy jakieś nowe rozszerzenie lub protokół.
Próbujmy zapewniać że ten nowy materiał nie powoduje konfliktów z
poprzednimi wersjami lub nie narusza zależności. Możemy także próbować
formalizować protokoły tak dużo jak to możliwe i wykrywać błędy zanim
spowodują jakieś problemy. Łatanie błędów jest naszym głównym zadaniem na
najbliższe lata. 

--[ 4.0 Nauka z przeszłości aka wniosek

Przeszłość mówi nam że protokół nie jest dostatecznie dobrze
wyspecyfikowany i prowadzi do błędów (bug, konflikt...). To może być czas na
zmianę naszych zwyczajów i próbę dodania czegoś na podstawie bilansu naszego
czasu. Np. do zaprojektowania naszych rzeczy bezpiecznie w umyśle. W tym
artykule spróbowałem pokazać wam że przez proste zrozumienie specyfikacji i
z pomocą podstawowej znajomości matematyki możesz:

- znaleźć usterkę o ogólnoświatowym wpływie,
- wykorzystać tą usterkę w elegancki sposób za pomocą prostej teorii,
- rozwijać sztukę fingerprintingu.

Mam nadzieję że to pomoże przekonać cię że teoria i formalne narzędzia są
niezbędną częścią obszaru bezpiecznego komputera. Następnym razem skupię się
na prostej formalnej metodzie znalezienia bugu. Mam nadzieję że tutaj
będziecie :)

--[ A Uznania

Najpierw chciałbym podziękować Romainowi Bottierowi za jego pomoc i jego
cierpliwość. Chcę także podziękować Plopsowi i Polucowi za wiarę we mnie.
Widzicie ludzie udało się nam!

Chcę także powiedzieć że biorę całkowitą odpowiedzialność za nie ujawnienie
polityki. Powiadomiłem głównych sprzedawców (Kernel.org, FreeBSD, OpenBSD,
Cisco...) miesiąc temu. Tak jak powiedziałem nie otrzymałem żadnej reakcji
więc dochodzę do wniosku że ich to nie obchodzi.

Referencje
*=*=*=*=*
[1] AT&T Steven M. Bellovin. A Technique for Counting NATted Hosts 
http://www.research.att.com/~smb/papers/fnat.pdf
[2] Jacobson, Braden, & Borman. RFC 1323 :TCP Extensions for High
Performance. 
[3] K. Egevang, Cray Communications, P. Francis. RFC 1631:
The IP Network Address Translator (NAT). 
[4] Bret McDanel. TCP Timestamping - Obtaining System Uptime Remotely
originally posted to Bugtraq Security Mailing List on March 11, 2001. 
[5] Michal Zalewski. p0f 2:Dr. Jekyll had something to Hyde. 
[6] Fyodor. Nmap - Free Security Scanner For Network Exploration &
Security Audits. 
[7] Antirez. dumbscan original BUGTRAQ posting (18 Dec 1998) 
[8] Microsoft. TCP timestamp in windows : KB224829. 
[9] Hafner, Katie, Matthew Lyon. Where Wizards Stay Up Late: The Origins of
the Internet.

Notatki 
*=*=*=*

(1) Punktem widzenia Microsoftu jest to że NAT nie jest bezpiecznym
mechanizmem więc nie chcą tego łatać.

(2) Jesteś zainteresowany moim analizatorem? Mam nadzeję nadzieję że
niedługo opublikuję teoretyczny artykuł o tym jak to działa. Mam także
nadzieję wypuścić wkrótce jakąś wersję tego. Co do pytania czy znalazłem
inne interesujące rzeczy odpowiedź brzmi: może muszę to sprawdzić
bardziej dogłębnie.

--[ B Dowód koncepcji 

/* 
* Dowód koncepcji: liczenie hostów za NAT-em przy pomocy timestamp
* Żeby skompilować ten plik potrzebujesz libpcap
* Wszelkie prawa zatrzeżone dla Elie Bursztein (lupin@zonart.net)
* Kompilacja udała się na FreeBSD 5.X i 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;

/* Łańcuch struktur list */
typedef struct listes_s {
struct listes_s *next;
void *elt;
} listes_t;

/* Struktury dla opcji TCP */

typedef struct { u_int32_t ts, ts_r; } timestamp_t;
typedef struct { timestamp_t *ts; } tcp_opt_t;

/* Struktury do składu danych */

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

/* Lista funkcji */

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("-- Poczatek listy NAT-owanych adresow IP --\n");

while (list) {
nat = (nat_box_t *) list->elt;
if (nat->nat > 1) {
addr.s_addr = nat->host;
printf("Przypuszczam ze %i komputerow rozdziela te same adresy
(%s)\n", nat->nat, inet_ntoa(addr));
}

list = list->next;
}

printf("-- Koniec listy NAT-owanych adresow IP --\n");
}

/* Funkcja używana do pobierania wszystkich opcji TCP.
* Prosty parser opcji TCP */

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; /* Bajt NOP */

case 2: 
i += 4; 
break;

case 3: 
i += 3; 
break;

case 4: 
i += 2; 
break;

case 5: /* pominięcie opcji SACK */
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);
}

/* Najbardziej interesująca funkcja... Tutaj możemy sie dowiedzieć czy
* pakiet TCP pochodzi od kogoś kogo już znamy!

* Prototyp:
* finc(sekundy) = aktualny_czas_pakietu - pierwszy_czas_pakietu <-
* czas_pomiedzy_dwoma_pakietami
* ts_inc = inc_table[i] * finc <-
* nasz_przypuszczalny_inkrementowany_timestamp_pomiedzy_dwoma_pakietami
* new_ts = pierwszy_timestamp + ts_inc <- new *
timestamp_ktory_powinnismy_miec_teraz!
*
* Teraz musimy poprostu porównać new_ts z aktualnym czasem timestampu.
* Możemy autoryzować marginesowy błąd od 0.5%.
*
* Nasza inc_table zawiera inkrementowany co sekundę timestamp dla wielu
* systemów operacyjnych. */

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[i] * finc;
new_ts = ts_inc + mach -> first_timestamp;
diff = ntohl(opt->ts->ts) - new_ts;

if (diff == 0) { /* Celny strzał! */
return (2);
}

tmp = 100. - (new_ts * 100. / ntohl(opt->ts->ts));

if (tmp < 0.)
tmp *= -1.;

if (tmp <= TIMESTAMP_ERROR_MARGIN) { /* Uaktualniony timestamp i czas */
mach -> first_seen = temps;
mach -> first_timestamp = ntohl(opt->ts->ts);
return (1);
}
}

return (0);
}

/* Zwykła funkcja sprawdzająca czy adres IP jest już w naszej liście. Jeżeli
* nie to jest to tylko nowe połączenie. */

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);
}

/* Ta funkcja powinna być wywoływana jeżeli pakiet z adresu IP został
* znaleziony, jeśli adres jest już w liście ale nie pasuje do żadnej
* wartości timestamp. */

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("Musisz byc rootem zeby uzywac tego narzedzia.\n");
exit (2);
}

if (--ac != 1) {
printf("Uzycie: ./natcount xl0\n");
return (1);
}

device = (++argv)[0];
pcap_lookupnet(device, &netp, &maskp, errbuf);
my_ip_addr.s_addr = (u_int32_t) netp;

printf("Uzywany interfejs IP %s: %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,"Blad nazywany pcap_compile\n");
exit(1);
}

if (pcap_setfilter(sniff,&fp) == -1) {
fprintf(stderr, "Blad ustawienia filtra\n");
exit(1);
}

pcap_loop(sniff, -1, callback_sniffer, NULL);
return (0);
}