next   previous   contents

Strutture dati

 Allo scopo di comprendere al meglio i dettagli implementativi descritti nelle pagine successive, vengono ora illustrate le principali strutture dati presenti in servknock.

 knock_t struttura dati utilizzata per memorizzare una sequenza di knock letta dal file di configurazione. Tutte le struct di questo tipo vengono mantenute in una lista di elementi di tipo PMList, accessibile dal puntatore globale PMList *knocks_list .

/* knock tuples struct */
typedef struct knock_struct
 {
  char name[128];
  u_short seqcount;
  u_char sequence[SEQ_MAX];
  u_short protocol[SEQ_MAX];
  time_t seq_timeout;
  PMList *start_command_list;
  time_t cmd_timeout;
  PMList *stop_command_list;
  u_short flag_fin;
  u_short flag_syn;
  u_short flag_rst;
  u_short flag_psh;
  u_short flag_ack;
  u_short flag_urg;
 } knock_t;

 name : il nome associato alla sequenza di knock
 seqcount : il numero di pacchetti da cui e' costituita la sequenza
 sequence : array dove sono definite le porte che costituiscono la sequenza di knock.
 protocol : array dove e' definito il protocollo di ogni pacchetti che costituisce la sequenza di knock. Valori ammessi sono IPPROTO_TCP, IPPROTO_UDP, IPPROTO_ICMP
 seq_timeout : numero di secondi entro i quali deve essere completata una sequenza di knock.
start_command_list : lista dei comandi da eseguire al riconoscimento della sequenza di knock.
 cmd_timeout : intervallo in secondi dal riconoscimento della sequenza, dopo il quale eseguire i comandi di chiusura.
 stop_command_list : lista dei comandi di chiusura che devono essere eseguiti dopo il tempo specificato in cmd_timout, dal momento del riconoscimento della sequenza
 flag_fin, flag_syn, flag_rst, flag_psh, flag_ack, flag_urg : valori che devono avere i flag dei pacchetti tcp compresi nella sequenza di knock.


 attempt_t : struttura utilizzata per tenere traccia dello stadio a cui e' arrivata una sequenza di knock per un particolare indirizzo IP. Per ogni indirizzo IP che dia inizio ad una sequenza viene infatti allocata una struct di questo tipo, ed aggiornata ad ogni nuovo pacchetto riconosciuto.
 Se il primo elemento risultasse valido per piu' di una possibile sequenza, ovviamente verranno allocate piu' strutture di questo tipo.
 Tutte gli elementi (almeno uno per ogni indirizzo IP dei richiedenti) allocati vengono mantenuti in una lista linkata di elementi di tipo PMList, e rimossi dopo che una sequenza viene completata od invalidata. Il puntatore usato per accedervi e' PMList *attempts_list .

/* knock attempt struct*/
typedef struct attempt
 {
  knock_t *knock;
  short stage;
  char src[16];
  time_t seq_start;
  u_char ipbyte[4];
  short ipstage;
  int is_in_ipstage
;   u_char* sequence_crypted;
 } attempt_t;

 knock :puntatore all'elemento di tipo knock_t, ove e' definita la sequenza di knock che si sta cercando di riconoscere
 stage : indica lo stadio raggiunto nel riconoscimento della prima parte di una sequenza di knock. Quando una sequenza viene completata o invalidata questo valore viene settato a -1 .
 src : array di caratteri contenente l'indirizzo IP del client che sta inviando la sequenza.
 seq_start : indica il momento in cui e' stato ricevuto il primo pacchetto della sequenza di knock, espresso come il numero di secondi trascorsi dalla mezzanotte del primo gennaio 1970. Se il timestamp di un nuovo pacchetto ricevuto meno seq_start risulta superiore al valore seq_timeout specificato per la sequenza, l'attempt verra' invalidato, e di conseguenza stage settato a -1.
 ipbyte : array, dove ogni elemento e' l'i-esimo byte dell'indirizzo IP del richiedente
 ipstage : indica lo stadio raggiunto nel riconoscimento della seconda parte di una sequenza di knock, ovvero quella che comprende i bytes dell'indirizzo IP del richiedente.
 is_in_ipstage : flag utilizzato per indicare se allo stadio attuale si sia gia' arrivati o meno ad analizzare la seconda parte della sequenza.
 sequence_crypted : l'intera sequenza di knock crittata. Poiche' il range di porte utilizzato e' di 255 elementi, ed i byte di un indirizzo IP vanno da zero a 255, ciascun elemento della sequenza puo' essere rappresentato da un carattere del set ASCII, e quindi la sequenza da una stringa.


 command_t : struttura usata per memorizzare i comandi associati ad ogni sequenza di knock. Tutte le strutture di questo tipo vengono mantenute in due liste distinte: la prima per i comandi da eseguire al momento del riconoscimento e la seconda dopo il trascorrere dell'intervallo cmd_timeout per quella specifica sequenza. Come al solito le due liste sono costituite da elementi di tipo PMList, e sono accessibili dai puntatori start_command_list e stop_command_list , membri dell'elemento knock_t allocato per quella sequenza.  L'unico membro della struttura command e' char *bin, puntatore alla stringa che rappresenta il comando da eseguire.

/*command struct*/
typedef struct command
 {
 char *bin;
 } command_t;


 portspan_t : struttura utilizzata per rappresentare i range di porte monitorati da servknock. Per ogni intervallo viene allocata una struct di questo tipo ed inserita in una lista puntata dal puntatore globale PMList *portspan_list .

/* port span struct*/
typedef struct portspan
 {
 u_int minrange;
 u_int maxrange;
 } portspan_t;

 minrange : numero della prima porta dell'intervallo
 maxrange : numero dalla porta finale dell'intervallo


 PMList : struttura definita in list.h, ed usata per le diverse liste linkate utilizzate nel programma.

/* your average linked list */
typedef struct __pmlist_t
 {
 void* data;
 struct __pmlist_t* prev;
 struct __pmlist_t* next;
 } PMList;

 data : puntatore al contenuto dell'elemento della lista.
 prev : puntatore al precedente elemento della lista
 next : puntatore all'elemento successivo della lista


 Le strutture dati seguenti invece definiscono gli header dei vari pacchetti riconosciuti da servknock. Per ragioni di portabilita' non vengono utilizzate direttamente le strutture dati definite negli header del kernel, ma si includono le definizioni delle strutture per i pacchetti interessati.

 ipv4_hdr : memorizza i dati dell'header IP dei pacchetti processati

/* IPv4 header, version 4, Static header size: 20 bytes */
struct ipv4_hdr
 {
 #if defined(BYTE_ORDER_LITTLE_ENDIAN)
  u_int8_t ip_hl:4, /* header length */
  ip_v:4; /* version */
 #elif defined (BYTE_ORDER_BIG_ENDIAN)
  u_int8_t ip_v:4, /* header length */
  ip_hl:4; /* version */
 #else
  #error "Please, edit Makefile and add -DBYTE_ORDER_(BIG|LITTLE)_ENDIAN"
 #endif
 u_int8_t ip_tos; /* type of service */
 u_int16_t ip_len; /* total length */
 u_int16_t ip_id; /* identification */
 u_int16_t ip_off;

 u_int8_t ip_ttl; /* time to live */
 u_int8_t ip_p; /* protocol */
 u_int16_t ip_sum; /* checksum */
 struct in_addr ip_src, ip_dst; /*source and dest address */
 };

 Il significato dei campi piu' importanti e' il seguente:

 ip_v : 4 bit che contengono la versione dell' internet protocol;
 ip_hl : 4 bit che identificano la lunghezza dell'header
 ip_tos : type of service 8 bit per settare le impostazioni del datagramma IP ( minimize delay, maximize throughput, ecc. );
 ip_len : e' la word indicante la lunghezza in byte dell'intero datagramma ( header + pacchetto incapsulato ) , compresi i dati e gli header dei protocolli di livello superiore.
 ip_id: e' un numero di due byte, impostato da chi invia il pacchetto e che serve al ricevente per identificare univocamente il datagramma IP e ricostruire i dati frammentati.
 ip_off : indica l'offset del pacchetto frammentato rispetto all'inizio del datagramma IP originale
 ip_ttl : numero che indica il limite massimo degli instradamenti effettuabili da un host all'altro
 ip_p : identifica il protocollo di livello superiore (6 per il protocollo udp, 17 per tcp, 1 per icmp) che verra' usato nel trasporto dei dati
 ip_sum : checksum calcolata sullo header
 ip_src : indirizzo IP del mittente
 ip_dst : indirizzo IP del destinatario.


 udp_hdr : struttura dati che memorizza i dati dell'header dei pacchetti UDP.

/* UDP header */
struct udp_hdr
 {
 u_int16_t uh_sport; /* source port */
 u_int16_t uh_dport; /* destination port */
 u_int16_t uh_ulen; /* length */
 u_int16_t uh_sum; /* checksum */
 };

 uh_sport : porta sorgente
 uh_dport : numero di porta a cui è destinato il pacchetto
 uh_ulen : lunghezza dell'intero pacchetto UDP ( dati + intestazione ). La lunghezza minima prevista e' 8 byte.
 uh_sum : somma di controllo, calcolata sia sull'header che sui dati.


 tcp_hdr: struttura dati utilizzata per mantenere i dati dell'header dei pacchetti TCP processati

/* TCP header (linux/tcp.h) */
struct tcp_hdr
 {
 u_int16_t th_sport; /* source port */
 u_int16_t th_dport; /* destination port */
 u_int32_t th_seq; /* sequence number */
 u_int32_t th_ack; /* acknowledgement number */
 #if defined (BYTE_ORDER_LITTLE_ENDIAN)
  u_int8_t th_x2:4, /* (unused) */
  th_off:4; /* data offset */
 #elif defined (BYTE_ORDER_BIG_ENDIAN)
  u_int8_t th_off:4, /* data offset */
  th_x2:4; /* (unused) */
 #else
  #error "Please, edit Makefile and add -DBYTE_ORDER_(BIG|LITTLE)_ENDIAN"
 #endif

 /*tcp flags*/
 u_int8_t fin:1, syn:1, rst:1, psh:1, ack:1, urg:1;

 u_int16_t th_win; /* window */
 u_int16_t th_sum; /* checksum */
 u_int16_t th_urp; /* urgent pointer */
};

 th_sport : porta sorgente da cui e' partito il pacchetto
 th_dport : porta di destinazione
 th_seq : numero di sequenza identificativo
 th_ack : numero di sequenza del pacchetto seguente
 th_off : lunghezza dell' header TCP in parole di 32 bit, se non vengono inserite opzioni la lunghezza dell' intestazione è di 20 byte, quindi il valore sara' 5
 fyn, rst, psh, ack, syn, urg : flag del pacchetto TCP
 th_win : dimensione della finestra, ovvero numero di byte che il destinatario e' disposto ad accettare. Essendo il campo limitato a 16 bit, il massimo numero di byte ammesso e' 65535
 th_sum : somma di controllo, calcolato sia sull'header che sui dati. Scritta dal mittente, deve essere verificata dal destinatario.
 th_urp : valido solo se il flag urg e' impostato. Indica un offset da aggiungere al numero di sequenza per capire quando terminino i dati importanti.
 Come si potra' notare in alcune strutture sono presenti delle direttive al preprocessore per verificare se siano definite o meno le costanti BYTE_ORDER_LITTLE_ENDIAN e BYTE_ORDER_BIG_ENDIAN, in modo da gestire i bitfield delle strutture stesse in base al sistema di rappresentazione. Con il sistema big-endian viene usato l'indirizzo di memoria maggiore per contenere il byte piu' significativo, mentre con il little-endian contiene il byte meno significativo. Per ottenere a compile-time questa informazione, servknock include il file bytesex.h ( copyright 2000,2001 Salvatore Sanfilippo antirez@invece.org )

 Per quanto riguarda Module Knock le strutture dati utilizzate sono del tutto identiche. Di particolare rilevanza tuttavia sono le strutture dati definite nell'header file hogwash.h, presente all'interno della cartella engine di Hogwash. Tutte le informazioni riguardanti interfacce, decoders, moduli, pacchetti ricevuti, etc. sono memorizzati infatti nella variabile globale Globals, la quale e' una struct C definita in questo modo (vengono riportati i campi di maggior interesse):

typedef struct global_vars{
  [...]
  char* ConfigFilename;
  [...]
  int PacketLimit;
  [...]
  PacketRec Packets[MAX_PACKETS];
  [...]
  InterfaceRec Interfaces[MAX_INTERFACES];
  int NumInterfaces;

  DecoderRec Decoders[MAX_DECODERS];
  int NumDecoders;
  int DecoderRoot;

  ModuleRec Modules[MAX_MODULES];
  int NumModules;

  FuncList* ShutdownFuncs;
} GlobalVars;

 ConfigFilename : nome del file di configurazione, contentente fra le altre cose, anche le opzioni del modulo di portknocking.
 PacketLimit : limite massimo di pacchetti da catturare
 Packets: array di MAX_PACKETS (valore di default 512) PacketRec, struttura utilizzata per la memorizzazione dei pacchetti ricevuti.
 Interfaces : array di MAX_INTERFACES InterfaceRec, struttura utilizzata per la memorizzazione delle caratteristiche delle interfacce di rete.
 NumInterfaces : numero di interfacce di rete dalle quali vengono catturati i pacchetti.
 Decoders : array di MAX_DECODERS DecoderRec, struttura utilizzata per la memorizzazione delle informazioni riguardanti i decoders, dei moduli a loro associati, e per la costruzione dell'albero dei decoders. (vedi 4.5)
NumDecoders : numero di decoders attivati.
DecoderRoot : ID numerico del decoder radice dell'albero dei decoders Modules : array di MAX_MODULES ModuleRec, struttura utilizzata per la memorizzazione dei moduli allocati
NumModules : numero di moduli allocati
ShutdownFuncs : puntatore alla tests di una lista di elementi FuncList, i quali contengono a loro volta dei puntatori a funzione. Tali funzioni vengono chiamate quando il processo Hogwash riceve un segnale di terminazione.

PacketRec : struttura dati utilizzata per la memorizzazione dei pacchetti ricevuti, e le informazioni ad essi relative

typedef struct packet_rec{
  int PacketSlot;

  int InterfaceNum;
  unsigned char* RawPacket;
  int PacketLen;
  struct timeval tv;

  DecoderData DecoderInfo[MAX_DECODER_DEPTH];
  int NumDecoderData;
  int BeginData;
} PacketRec;

 PacketSlot : posizione del pacchetto all'interno del vettore Packets
 InterfaceNum : indica l'interfaccia dalla quale il pacchetto e' stato catturato
 RawPacket : buffer in cui viene memorizzato il pacchetto e su cui verranno applicati i decoders
 PacketLen : lunghezza del pacchetto
 tv : struttura che tiene memoria del timestamp in cui il pacchetto e' stato ricevuto
 DecoderInfo : vettore di elementi DecoderData, utilizzati per tener traccia dei puntatori agli header del pacchetto, estratti per mezzo dei vari decoders.
 NumDecoderData : numero dei decoders applicati al pacchetto
 BeginData : primo bit del pacchetto non ancora decodificato

ModuleRec : struttura dati utilizzata per tener traccia dei moduli

typedef struct module_rec{
  char Name[MAX_NAME_LEN];
  int ID;

  struct module_rec* Next;

  int (*ParseArg) (char* Arg);
  void (*ModuleFunc) (int PacketSlot);
} ModuleRec;

 Name : nome del modulo
 ID : identificatore numerico del modulo
 Next : puntatore ad un elemento ModuleRec, utilizzato per la costruzione di liste di moduli associate ai decoders
 ParseArg : puntatore alla funzione di parsing del modulo
 ModuleFunc : puntatore alla funzione del modulo che viene chiamata per ogni pacchetto ricevuto

next   previous   contents