Implementação de uma ferramenta de monitoração de tráfego de rede TCP/IP usando Winsock

Edilei Marcos de Oliveira <edileioliveira@hotmail.com>

Instituto de Informática
Universidade Norte do Paraná

1. Introdução
2. A ferramenta de monitoração
3. Modelo em camadas TCP/IP
4. A biblioteca Winsock
5. Decodifição dos datagramas IP
6. Apresentação da ferramenta de monitoração
7. Conclusão
Referências bibliográficas

1. Introdução

O objetivo principal deste artigo é apresentar as rotinas da Winsock necessárias para a implementação de uma ferramenta de monitoração de tráfego de rede TCP/IP em uma rede ETHERNET. Esta ferramenta de monitoração trabalha com a captura, decodificação e cópia dos datagramas IPv4 em buffer, obedecendo às regras de captura definidas em filtro. Para a captura de todos os pacotes que estiverem transitando pelo segmento de rede, o dispositivo de comunicação deve ser configurado para operar em modo promíscuo. Para isto, no Windows, usa-se um driver de captura externo (de terceiros) ou usa-se um socket promíscuo configurado através das rotinas da Winsock 2.2 do Windows 2000 ou superior. Ferramentas como TCPDUMP e ETHEREAL utilizam driver de captura externo para rodarem, porém, a partir do Windows 2000, pode-se usar a API Winsock 2.2 ou superior do próprio sistema operacional para capturar os datagramas IP. Os códigos aqui apresentados estão em C++ sobre o ambiente Visual C++ 6.0.

^

2. A ferramenta de monitoração

Através da análise dos pacotes TCP/IP capturados da rede, o administrador de rede pode gerenciar melhor sua rede, pela análise dos pacotes ICMP; realizar mapeamento das conexões TCP/IP de um determinado host, pela análise de tráfego originado ou destinado a determinado host; verificar níveis de segurança, observando se há dados secretos que não estão sendo criptografados; descobrir os serviços (SMTP, DNS, FTP, HTTP e outros) que os hosts da rede ETHERNET oferecem ou requisitam.

Como o domínio de captura pode ser muito amplo, é interessante estabelecer algumas restrições de captura. Algumas regras de filtragem podem ser definidas: por endereço, por tipo de protocolo, por porta de comunicação ou por string. Desta forma, a análise fica concentrada nos pacotes que safisfazem as regras do filtro.

As seguintes etapas são realizadas pela ferramenta:

  1. captura o pacote;
  2. decodifica o pacote;
  3. compara o pacote capturado com as definições do filtro de pacotes;
  4. se o pacote não satisfizer as regras do filtro de pacotes, o pacote será ignorado;
  5. se o pacote satisfizer as regras do filtro de pacotes, a ferramenta copiará o pacote em buffer;
  6. continua a captura até encher o buffer ou, se antes disso, o usuário solicitar o término da captura.

^

3. Modelo em camadas TCP/IP

Existem dois modelos dominantes sobre a divisão dos protocolos em camadas. O primeiro foi proposto pela ISO, é conhecido como modelo OSI composto por 7 camadas. O segundo modelo, mais adequado a realidade, designa que a pilha de protocolos TCP/IP (tabela 1) é composta por 4 camadas ou níveis: aplicação, transporte, rede, física.

Aplicação
HTTP, SMTP, POP3, DNS, FTP, SNMP, DHCP, telnet, EGP e outros
Mensagens (Correspondem às camadas OSI 5, 6 e 7) Protocolos usados pelos programas aplicativos. Os programas aplicativos passam as mensagens apropriadas ao protocolo de transporte para transmissão.
Transporte
TCP, UDP
Pacotes ou mensagens (Correspondem à camada OSI 4) TCP: protocolo de transferência confiável, com conexão e com controle de fluxo.
UDP: protocolo de transferência de mensagens não confiável, sem conexão e sem controle de fluxo.
Rede
IP, ICMP
Datagramas IP (Correspondem à camada OSI 3) IP: protocolo de roteamento, sem conexão.
ICMP: é parte do datagrama IP, protocolo para deteção de erros de rede usado por hosts e roteadores.
Física
Drivers de dispositivo e protocolos de acesso ao meio
Quadros de rede (Correspondem às camadas OSI 1 e 2) Os protocolos da camada física recebem e transmitem os datagramas IP através da rede conectada.

Tabela 1 - Camadas TCP/IP

Na perspectiva da ferramenta de monitoração, o diagrama 1 ilustra o relacionamento entre os protocolos. O socket em modo promíscuo recebe o datagrama IP, representado em dois campos: cabeçalho e área de dados. O cabeçalho do datagrama IP possui um campo que indica qual pacote (TCP, UDP, ICMP ou outros) está encapsulado em sua área de dados e outro campo que especifica o tamanho do cabeçalho em palavras de 32 bits, permitindo determinar o início da área de dados do datagrama. Os pacotes TCP ou UDP contêm um cabeçalho e podem conter uma área de dados (alguns pacotes não possuem área de dados porque transportam somente as informações de controle). Os cabeçalhos dos pacotes TCP e UDP possuem campos de porta de origem e porta de destino que identificam os programas aplicativos nas extremidades da conexão, um campo de comprimento do cabeçalho que permite determinar o início da área de dados quando existir.

Relacionamento entre os protocolos TCP/IP

Diagrama 1 - Relacionamento entre os protocolos TCP/IP

^

4. A biblioteca Winsock

Todas as operações de recebimento de datagramas IP serão realizadas através de chamadas às rotinas da Winsock 2.2 , que é uma biblioteca do Windows que provê recursos de comunicação de dados. Antes que comecemos a utilizar a biblioteca Winsock, nós devemos declarar o cabeçalho: #include <winsock2.h> e importar a biblioteca ws2_32.lib. O trecho de código abaixo inicializa a biblioteca para uso.

/*armazena informações sobre a inicialização da Winsock */
WSAData info;

WORD versao;
versao= MAKEWORD(2,2);

/*inicialização da biblioteca WS2_32.DLL versão 2.2*/
if (WSAStartup(versao, &info) != 0) { MessageBox(NULL, "A inicialização da Winsock falhou!", "WSAStartup", MB_OK);
}

A rotina WSAStartup serve para inicializar a biblioteca Winsock. O parâmetro versao informa a versão da biblioteca requisitada e o parâmetro info obtém informações sobre a biblioteca inicializada.

Depois de terminar de usar a biblioteca Winsock a aplicação deverá chamar a rotina WSACleanup para desregistrar a biblioteca WS2_32.DLL e liberar os recursos alocados para ela.

/* libera recursos da biblioteca alocada */
WSACleanup();

Após a inicialização da biblioteca Winsock é executada a criação de um socket.

/*criação do socket */
sd = socket(AF_INET, SOCK_RAW, IPPROTO_IP);

O primeiro argumento (AF_INET) da função socket especifica que o protocolo a ser usado pelo socket pertence a família TCP/IP. O segundo argumento (SOCK_RAW) especifica que os datagramas IP podem encapsular qualquer tipo de protocolo, inclusive protocolos que estão em fase experimental. O terceiro argumento (IPPROTO_IP) especifica o protocolo IP. A função socket retorna um descritor de socket.

Depois da criação do socket é necessário configurá-lo através da função bind. O primeiro argumento da função bind é o descritor de socket para a captura dos pacotes. Para o segundo argumento, passa-se o endereço da variável interfaceRecepcao do tipo SOCKADDR_IN, que contém as informações de endereço IP da interface utilizada na captura, a família de protocolos (TCP/IP) e a porta de operação, e realiza-se um cast com a estrutura SOCKADDR. O terceiro argumento é o comprimento da variável interfaceRecepcao.

A função WSAIoctl é usada para configurar o socket ou para retornar parâmetros operacionais associados ao socket. No caso, usamos a palavra SIO_RCVALL para configurar o socket em modo promíscuo que habilita o socket a receber todos os datagramas IP da rede. Privilégios de administrador são exigidos sobre o computador local para configurar este socket. SIO_RCVALL está disponível em Windows 2000 ou superior. O comando SIO_RCVALL, antes de ser usado, deve ser definido como: #define SIO_RCVALL WSAIOW(IOC_VENDOR,1).

...
/*configurações para o socket */
SOCKADDR_IN interfaceRecepcao; 

/* configuração da interface de recepção */

/* endereço IP da inferface de captura */
interfaceRecepcao.sin_addr.S_un = enderecoIp.S_un; /* comunicação TCP/IP */ interfaceRecepcao.sin_family = AF_INET; interfaceRecepcao.sin_port = htons(0); /* associa o endereço IP da interface de captura ao socket */ if (bind(sd, (SOCKADDR *) &interfaceRecepcao, sizeof(interfaceRecepcao))==SOCKET_ERROR) { wsprintf(mensagem,"bind() falhou. Código de erro: %d",WSAGetLastError()); MessageBox(NULL,"Função bind() falhou","Aviso!",MB_OK); return 0; } int comprimentoBufferEntrada; comprimentoBufferEntrada = 1; /*configura o socket para modo promíscuo */ if (WSAIoctl(sd, SIO_RCVALL,&comprimentoBufferEntrada, sizeof(comprimentoBufferEntrada),NULL,0,&bytesRetorno,NULL, NULL) == SOCKET_ERROR) { wsprintf(mensagem, "WSAIoctl(SIO_RCVALL) falhou. Código de erro: %d", WSAGetLastError()); MessageBox(NULL,"Função WSAIoctl() falhou","Aviso!",MB_OK);
return 0; } ...

Devemos chamar a função WSARecv para receber o datagrama IP através do socket. O primeiro argumento da função WSARecv é o descritor de socket que foi configurado para modo promíscuo. O segundo argumento é um ponteiro para um array de estrutura WSABUF. Cada estrutura WSABUF possui um ponteiro para um buffer e o comprimento do buffer em bytes. O terceiro argumento está informando que o array de WSABUF possui tamanho um. O quarto argumento é um ponteiro para um número que armazena o número de bytes recebidos após a chamada WSARecv se completar.

...
char mensagem[100];
unsigned long comprimento;
unsigned long flags;
flags=0;
comprimento=0;
  
/* recebe os pacotes na estrutura wsabuf */
if (SOCKET_ERROR==WSARecv(sd,wsabuf, 1, &comprimento, &flags, NULL, NULL))
  {
  wsprintf(mensagem, "WSARecv() falhou. Código de erro: %d",WSAGetLastError());
  MessageBox(NULL, mensagem, "Problema no recebimento!", MB_OK);
  return 0;
  }
wsabuf->len=comprimento;
...

^

5. Decodifição dos datagramas IP

Logo após que a chamada WSARecv se complete apropriadamente, o sistema começa a realizar a decodificação parcial do datagrama IP, que consiste numa seqüência de bits brutos, para verificar se o pacote atende às regras de filtragem. Caso o pacote atenda às regras do filtro, ele é bufferizado, senão é descartado. Este processo de captura e decodificação repete-se até que o buffer esteja cheio ou o usuário interrompa o processo de captura.

Como o pacote recebido sempre é um datagrama IP, a decodificação se inicia pela análise dos campos do seu cabeçalho que contêm informações sobre o protocolo que sua área de dados transporta. Desencapsula-se o datagrama IP e se avalia o protocolo que está nele contido, começando-se pela análise dos campos do seu cabeçalho que contêm informações sobre o possível protocolo contido em sua área de dados.

O diagrama 2 apresenta as classes de decodificação dos protocolos IP, TCP, UDP e ICMP (só o cabeçalho comum entre os vários tipos de mensagens ICMP) em notação UML. A relação de dependência das classes de decodificação TCP, UDP e ICMP com a classe IP, é considerada devido ao contexto de decodificação.

Após a chamada à função WSARecv se completar adequadamente, vamos decodificar parcialmente o datagrama IP que está no buffer em uma estrutura do tipo WSABUF. Primeiramente, decodificamos apenas alguns campos importantes para defrontarmos com as regras de filtragem dos pacotes.O trecho de código a seguir contém métodos para a decodificação parcial do datagrama IP pertencentes à classe de decodificação CDatagramaIP.

...

/* recebe o datagrama IP em formato bruto para decodicação */
void CDatagramaIP::decodificarParcialmenteDatagramaIP(WSABUF *wsabuf)
 {
 versao_e_comprimento = (BYTE) wsabuf->buf[0];
 tamanhoTotal = (BYTE) wsabuf->buf[2]*256 + (BYTE) wsabuf->buf[3];>

 protocolo = (BYTE) wsabuf->buf[9];
 ipOrigem = (BYTE) wsabuf->buf[12]*16777216 +
 (BYTE) wsabuf->buf[13]*65536 +
 (BYTE) wsabuf->buf[14]*256 +
 (BYTE) wsabuf->buf[15];

 ipDestino = (BYTE) wsabuf->buf[16]*16777216 +
 (BYTE) wsabuf->buf[17]*65536 +
 (BYTE) wsabuf->buf[18]*256 +
 (BYTE) wsabuf->buf[19];

 /* dados do datagrama IP*/
 dados.len=tamanhoTotal - obterTamanhoCabecalho();
 dados.buf=&wsabuf->buf[obterTamanhoCabecalho()];
 }

/* obtém o tamanho do cabecalho em bytes */
unsigned char CDatagramaIP::obterTamanhoCabecalho() 
 {
 unsigned char aux; 
 aux = versao_e_comprimento;
 /*determina os valores dos 4 bits menos significativos */
 aux = aux & 15;
 return aux*4;
 }

/* obtém tamanho total do datagrama IP
unsigned short CDatagramaIP::obterTamanhoTotal()
 { 
 return tamanhoTotal;
 }
 
/* obtém código do protocolo encapsulado no datagrama IP
int CDatagramaIP::obterCodigoProtocolo()
 {
 return protocolo;
 }
 
/* obtém o endereço IP de destino do datagrama no formato de um número inteiro */
unsigned int CDatagramaIP::obterIpDestinoNumerico()
 { 
 return ipDestino;
 }
 
/* obtém o endereço IP de origem do datagrama no formato de um número inteiro */
unsigned int CDatagramaIP::obterIpOrigemNumerico()
 {
 return ipOrigem;
 }
  
/* obtém os dados do datagrama IP */
WSABUF CDatagramaIP::obterDadosIP()
 {
 return dados;
  }
  ...
Classes de decodificação TCP/IP

Diagrama 2 - Classes de decodificação TCP/IP

^

6. Apresentação da ferramenta de monitoração

A figura 1 mostra um conjunto de datagramas IP capturados da rede e que contêm em um comum a string “rnp” em sua área de dados. O pacote de número 22, que está em foco, por exemplo, tem 1500 bytes de tamanho, encapsula os protocolos TCP e HTTP, foi transmitido do host 200.130.9.7, usando a porta de conexão 80 (serviço HTTP), para o host 200.186.215.183 na porta de conexão 1249. Podemos verificar, na área de dados do pacote, que se trata de um texto legível, isto é, sem criptografia da informação.

Tela da ferramenta contendo pacotes capturados

Figura 1 - Tela da ferramenta contendo pacotes capturados

7. Conclusão

Este artigo teve como foco a apresentação das primitivas da API Winsock 2.2 para criação e uso de um socket em modo promíscuo para desenvolvimento de uma ferramenta de monitoração do tráfego de rede TCP/IP sobre uma rede ETHERNET. A avaliação do tráfego de rede permite: gerenciar melhor a rede, verificar níveis de segurança, mapear conexões e descobrir os serviços oferecidos ou utilizados pela rede.

^

Referências bibliográficas

[1] COMER, D. E.; STEVENS, D. L. Internetworking with TCP/IP. New Jersey: Prentice-Hall, v.3, 1997.

[2] COMER, D. E.; STEVENS, D. L. Interligação em Rede com TCP/IP. Rio de Janeiro: Campus, v.1, 1998.

[3] WELCOME TO THE MSDN LIBRARY. Welcome to the MSDN Library. Disponível em: <http://msdn.microsoft.com/library>. Acesso em 01 set. 2002.

[4] CAPTURE PACKETS WITH WINSOCK. Capture packets with Winsock. Disponível em: <http://www.securityfriday.com/Topics/caputure_packet.html>. Acesso em 22 abr. 2002.

[5] POSTEL, J. User Datagram Protocol. RFC 768, aug. 1980. Disponível em: <http://www.ietf.org/rfc/rfc0768.txt?number=768>. Acesso em 22 abr. 2002.

[6] INFORMATION SCIENCES INSTITUTE. UNIVERSITY OF SOUTHERN CALIFORNIA. Internet Protocol. RFC 791, sep. 1981. Disponível em: <http://www.ietf.org/rfc/rfc0791.txt?number=791>. Acesso em 22 abr. 2002.

[7] POSTEL, J. Internet Control Message Protocol. RFC 792, sep. 1981. Disponível em: <http://www.ietf.org/rfc/rfc0792.txt?number=792>. Acesso em 22 abr. 2002.

[8] INFORMATION SCIENCES INSTITUTE. UNIVERSITY OF SOUTHERN CALIFORNIA. Transmission Control Protocol. Disponível em: <http://www.ietf.org/rfc/rfc0793.txt?number=793>. Acesso em 22 abr. 2002.