Valeu ai pedro... vai ajudar muito o entendimento =D
--- Em [email protected], PEdroArthur_JEdi <[EMAIL PROTECTED]> escreveu > > Boa tarde galera, > > A alguns messes escrevi um guia do código do HLBR para meu orientador > aqui na universidade. Estou enviando-o como anexo para que quem quiser > possa dar uma olhada. Ele é baseado na versão 1.6 do código. Sempre > que puder vou tentar melhorá-lo e mandar aqui pra vocês. Espero que > gostem! Qualquer dúvida podem mandar pra lista, afinal sua dúvida pode > ser a de outro participante. E quem quiser desenvolver, é só falar! > > -- > PEdroArthur_JEdi > > Nunca acredite num sistema que você não conhece o código fonte! > Never trust a system you don't have sources for! > > Após ler as configurações do sistemas, o HLBR entra em seu loop principal para iniciar a verificação dos pacotes que trafégam em suas interfaces. Esse comportamento é observável através do arquivo fonte engine/main_loop.c (int MainLoop()). > > A função MainLoop tem por principal função verificar se o sistema está configurado para usar threads ou não. Em caso afirmativa, MainLoop desviará sua execução para a função int MainLoopThreaded(), caso contrário, será chamada a função int MainLoopPoll(). > > engine/main_loop.c: > 362: if (!Globals.UseThreads){ > 363: for (i = 0 ; i < Globals.NumInterfaces ; i++){ > 364: if (!Globals.Interfaces[i].IsPollable){ > 365: printf("Error. All interfaces must be > able to poll in single thread mode.\n"); > 366: return FALSE; > 367: } > 368: } > 369: return MainLoopPoll(); > 370: }else{ > 371: return MainLoopThreaded(); > 372: } > > MainLoopThread tentará iniciar uma thread para cada interface de rede que será usada pelo HLBR, retonando FALSE caso não consiga lançar uma das threads: > > engine/main_loop.c: > 337: for (i = 0 ; i < Globals.NumInterfaces ; i++) > 338: if (!StartInterfaceThread(i)){ > 339: printf("Couldn't start thread for interface\n"); > 340: return FALSE; > 341: } > > Apartir desse momento, o HLBR começará a aceitar pacotes para análise. MainLoopThreaded chamará a função void ProcessPacketThread(void *), a qual é reponsável por manter um laço infinito, representando uma relação de produtor/consumidor com os threads das interfaces em meio ao uso da fila de pacotes pendentes (Globals.Packets[MAX_PACKETS].status == PACKET_STATUS_PENDING definido em engine/hlbr.h). O excerto de código reponsável por essa função se encontra abaixo: > > engine/main_loop.c: > 242: while (!Globals.Done) { > 243: PacketSlot = PopFromPending(); > 244: > 245: if (PacketSlot != PACKET_NONE) { > 246: ProcessPacket(PacketSlot); > 247: } else { > 248: IdleFunc(); > 249: } > 250: } > > Podemos ver que enquanto a variável Globals.Done não for verdadeira, a thread em questão ficará aguardando por pacotes. A cada iteração ela irá ceder seu tempo de processamento para outra thread caso não tenha nenhum pacote aguardando, ou, dado o número do pacote (int PacketSlot), o fluxo será desviado para a função int ProcessPacket(int), responsável por encaminhar o pacote para decodificação (arquivo fonte decoders/decode.c), coletar algumas estatísticas dos pacotes, chamar as funções de ação, encaminhar o pacote para seu destino e retornar um slot vazio a fila de pacotes ociosos. > > engine/main_loop.c: > 191: if (!Decode(Globals.DecoderRoot, PacketSlot)){ > 192: printf("Error Processing Packet\n"); > 193: } > > O procedimento acima encaminha o pacote para a decodificação. Globals.DecoderRoot é um inteiro que representa o primeiro decodificador ao qual um pacote deve passar. Normalmente, Globals.DecoderRoot contém o valor do DecoderInterface (decoders/decode_interface.c). PacketSlot representa o número do pacote a ser processado. > > engine/main_loop.c: > 196: PacketSec++; > 198: if (GetDataByID(PacketSlot, TCPDecoderID, &data)) > 199: TCPSec++; > 200: else if (GetDataByID(PacketSlot, UDPDecoderID, &data)) > 201: UDPSec++; > 203: if (Globals.Packets[PacketSlot].tv.tv_sec != LastTime){ > 204: Globals.PacketsPerSec = PacketSec; > 205: Globals.TCPPerSec = TCPSec; > 206: Globals.UDPPerSec = UDPSec; > 208: PacketSec = 0; > 209: TCPSec = 0; > 210: UDPSec = 0; > 212: LastTime = Globals.Packets[PacketSlot].tv.tv_sec; > 213: } > > Coleta estatísticas sobre a rede com informações baseadas nos contadores dos pacotes. > > engine/main_loop.c > 215: if (!BitFieldIsEmpty(p->RuleBits,Globals.NumRules)){ > 219: if (!PerformActions(PacketSlot)){ > 220: printf("Failed to execute the actions\n"); > 221: } > 222: } > > Verifica se alguma regra conferiu com o pacote. > > engine/main_loop.c > 224: RouteAndSend(PacketSlot); > > Envia o pacote pela rede. > > engine/main_loop.c > 225: ReturnEmptyPacket(PacketSlot); > > Retorna o pacote vazio a lista de ociosos. > > Dentre os procedimentos chamados acima, int Decode(int, int), definido em decoders/decode.c, tem predominância no comportamento do HLBR. Ele é responsável por encaminhar recursivamente os pacotes por entre os decodificadores até que se tenha uma decisão quanto ao destino do pacote. O código reproduzido abaixo representa a chamada de decodificação do pacote: > > decoders/decode.c: > 277: p->DecoderInfo[DecoderID].Data = Globals.Decoders[DecoderID].DecodeFunc(PacketSlot); > > Para entender melhor o que está acontecendo, devemos compreender o que é um decoder. Um decoder nada mais é que uma estrutura de dados onde são armazenados o nome do decoder, seu ID, sua mascará de dependência, testes associados, módulos associados, decoders filhos, decoder pai, próximo filho (campo acessado para escrita apenas pela função int DecoderAddDecoder(int, int)), uma função de decodificação, uma função de dealocação, uma função de configuração e sua flag de atividade. A estrura de dados encontra-se definida no arquivo fonte engine/hlbr.h, e reproduzida abaixo: > > engine/hlbr.h: > 249: typedef struct decoder_rec{ > 250: char Name[MAX_NAME_LEN]; > 251: int ID; > 252: unsigned char DependencyMask[MAX_RULES/8]; > > 253: struct test_rec* Tests; > 254: struct module_rec* Modules; > 255: struct decoder_rec* Children; > 256: struct decoder_rec* Parent; > 257: struct decoder_rec* NextChild; > 259: void* (*DecodeFunc) (int PacketSlot); > 260: void (*Free) (void *pointer); > 261: int (*ConfigFunction) (FILE *fp); > 263: char Active; /*true if anything > actually uses it*/ > 264: } DecoderRec; > > Outra estrutura de dados importante para que possamos prosseguir é a PacketRec (engine/hlbr.h). PacketRec possui todas as informações relevantes a um pacote. Dentre elas, as mais importantes são RawPacket, representando o pacote de dados em forma bruta, BeginData, que representa o offset onde o primeiro byte não decodificado se encontra, PacketLen, comprimento do pacote, PassRawPacket, flag de bloqueio, e sua mascára de bits. > > engine/hlbr.h: > 160 typedef struct packet_rec { > 161 int PacketSlot; > 162 unsigned int PacketNum; > 164 int InterfaceNum; > 165 int TargetInterface; > 167 unsigned char* RawPacket; > 168 char Pad[2]; > 169 unsigned char > TypicalPacket[TYPICAL_PACKET_SIZE]; > 170 char LargePacket; > 171 int PacketLen; > 173 unsigned char RuleBits[MAX_RULES/8]; > 174 struct timeval tv; > 176 DecoderData DecoderInfo[MAX_DECODER_DEPTH]; > 177 int DecodersUsed[MAX_DECODERS]; > 178 int NumDecoderData; > 180 int BeginData; > 183 char PassRawPacket; > 184 int SaveCount; > 187 char Status; > 189 pthread_mutex_t Mutex; > 190 int LockID; > 192 struct port_pair* Stream; > 193 } PacketRec; > > Com isso, podemos ver que o código executado na linha 277 de decoders/decode.c coleta os dados importantes do pacote para um dado decoder. Caso o pacote seja irrelevante ao decoder em questão (sua decodificação retorne NULL), todas as regras que dependem desse decoder são marcadas como inativa. Isso ocorre em: > > decoders/decode.c > 281: if (p->DecoderInfo[DecoderID].Data) { > /* And the code Flows */ > 307: } else { > 309: NotAndBitFields(p->RuleBits, Globals.Decoders[DecoderID].DependencyMask, p->RuleBits, Globals.NumRules); > 310: return TRUE; > 311: } > > Caso contrário, o pacote possua informações relevantes ao decoder, esse último é marcado como em uso e a função se encarrega de chamar os testes associados: > > decoders/decode.c > 287: p->DecodersUsed[p->NumDecoderData++] = DecoderID; > 290: test = Globals.Decoders[DecoderID].Tests; > 292: while (test) { > 293: if (test->Active) > 294: if (test->TestFunc) > 295: test->TestFunc(PacketSlot, > test->TestNodes); > 296: test = test->Next; > 297: } > > Testes também são estrutura de dados. TestRec armazena o nome do teste, o nome dos testes na escrita de regras, seu ID, o ID do decodificador associado, os nós de teste (estrutura explicada mais abaixo), um ponteiro para o proximo teste, a mascára de dependência, uma função para adição de nós de teste, a função de teste, uma função para finalizar a adição de testes e uma função reservada para uso futuro. > > engine/hlbr.h: > 220: typedef struct test_rec{ > 221: char Name[MAX_NAME_LEN]; > 222: char ShortName[MAX_NAME_LEN]; > 223: int ID; > 224: int DecoderID; > 225: char Active; > 226: TestNode* TestNodes; > 227: struct test_rec* Next; > 228: unsigned char DependencyMask[MAX_RULES/8]; > 230: int (*AddNode)(int TestID, int > RuleID, char* Args); > 231: int (*FinishedSetup)(); > 232: int (*TestFunc)(int PacketSlot, > TestNode* Nodes); > 233: int (*TestStreamFunc)(int > PacketSlot, TestNode* Nodes); > 234: } TestRec; > > Os arquivos de regras formam os nós de um PacketRec. Como por exemplo: > > rules/www.rules > 10: <rule> > 11: ip dst(www) > 12: tcp dst(80) > 13: tcp regex(GET[ -~]+\.asp($|/|\&|\?)) > 14: message=(www-1) .asp request > 15: action=action1 > 16: </rule> > > O que dizemos é: ao decodificador IP, adicionar um nó do teste DST com o valor "www". Ao decodificador TCP, adicionar um nó do teste DST com o valor 80 e adicionar um nó ao teste REGEX com o argumento "GET[ -~]+\.asp($|/|\&|\?)". Esses nós são armazenados em estruturas TestNode e enfileirados em meio ao uso de ponteiros: > > engine/hlbr.h > 209: typedef struct test_node{ > 210: int RuleID; > 211: void* Data; > 212: struct test_node* Next; > 213: } TestNode; > > Portanto, de forma bastante abstrata, na regra mostrada mais acima teriámos: > > TestNode {10, www, *} > TestNode {10, 80, *} > TestNode {10, GET[ -~]+\.asp($|/|\&|\?), *} > > Cada qual associado a seus respectivos decoders. > > Voltando para a função Decode, após os testes, é checado se ainda existem regras a serem conferidas. Caso não haja, a função retorna e podemos procurar por outro pacote para analisar, caso haja, devemos passar o pacote adiente, seguinda a hierárquia dos decodificadores. O seguinte trecho de código concretiza essa idéia: > > decoders/decode.c > 326: child = Globals.Decoders[DecoderID].Children; > 327: while (child) { > 328: if (!Decode(child->ID, PacketSlot)) { > 329: fprintf(stderr, "Decoder %s failed\n", > child->Name); > 330: } > 331: child = child->NextChild; > 332: } > > Estamos chamando a função Decode recursivamente, passando dessa vez como parâmetros o DecoderID do decoder filho e o pacote atual. Esse processo irá se repetir até que a condição de não haver mais regras seja atingida: > > decoders/decode.c > 314: if (!BitFieldIsEmpty(p->RuleBits, Globals.NumRules)) { > 322: return TRUE; > 323: } >
