5G Imagem Destaque

Agora que já conhecemos o Scapy, podemos criar ferramentas de rede com ele. Se você não sabe do que eu estou falando, clique aqui!

Todo o código exposto neste artigo se encontra em meu github, você pode acessá-lo através da URL abaixo:

https://github.com/decastromonteiro/GTPv1-Echo-Monitoring/

Visão Geral

A proposta neste artigo é criarmos juntos um monitor GTP. Pra quem não sabe, o GTP (GPRS Tunneling Protocol) é o protocolo de rede mais importante nas redes móveis, ele é responsável por todo o controle de sessão do usuário, entre suas tarefas está manter o usuário conectado mesmo quando ele está em deslocamento.

Portanto, é muito importante, numa rede móvel (Packet Core), monitorarmos as interfaces que utilizam o protocolo GTP.

O próprio protocolo, em sua especificação, define um procedimento de monitoramento, realizado através de duas mensagens:

  • GTP Echo Request
  • GTP Echo Response

O documento também define que a mensagem GTP Echo Request só deve ser enviada no máximo de 1 em 1 minuto.

Por sorte, o Scapy já possui o protocolo GTP definido, portanto vamos utilizá-lo para implementar nossa ferramenta.

Importando o módulo GTP

Na primeira linha do código, estamos importando todas as funções e classes padrões do scapy.

Na segunda linha do código, estamos importando um protocolo que não é padrão do scapy, porém foi feito por algum membro da comunidade e adicionado à biblioteca.

Neste caso, estamos importando especificamente o protocolo GTPv1 que corresponde ao módulo denominado gtp.

Além desses dois blocos de importação, iremos utilizar as seguintes bibliotecas:

Definindo os argumentos

A biblioteca argparse nos ajuda a utilizar argumentos passados na chamada do script python como variáveis deste script.

Vamos pensar em que tipos de opções queremos que o usuário possua ao iniciar nosso monitor GTP.

Lista de IPs

Para inciar, precisamos que o usuário forneça uma lista de IPs para nosso script, a fim de sabermos quais interfaces iremos monitorar. Portanto, vamos iniciar nosso parser.

Pronto, criamos o argumento -i que irá receber uma string como parâmetro contendo uma lista de IPs separados por vírgula, por exemplo:

Adicionando os demais argumentos

Além da lista de IPs, vamos utilizar outros argumentos. Queremos também que seja escolha do usuário se ele precisa criar arquivos de log, enviar a saída do log para o console, se irá monitorar a lista de IPs passada infinitamente e também se o nível do log será DEBUG ou não.

Portanto, vamos adicionar os demais argumentos ao nosso código.

Pronto, agora que já terminamos de definir quais argumentos o usuário poderá adicionar à chamada do nosso script, vamos iniciar as chamadas para a biblioteca logging.

Iniciando nosso “Logger”

Em resumo, o “logger” é o nosso gerador de logs, é sempre interessante reservamos um bloco no ínicio do nosso código para iniciarmos as chamados aos geradores de logs para que isso fique bem claro e não passe desapercebido.

O python já nos fornece uma biblioteca padrão para geração de logs, portanto vamos utilizá-la em nosso código. Chamaremos nosso gerador de log de “gtp_echo_monitoring” e iremos atribuir formato padrão e nível padrão à ele.

Uma vez realizada essa configuração, podemos começar a verificar os argumentos utilizados pelo nosso usuário, adicionando comportamentos derivados dessas entradas.

Calma, eu sei que introduzimos um monte de novos conceitos com o código acima, vamos explicá-los para que fique clara a função de cada bloco acima.

Primeramente, vamos verificar se o usuário utilizou o argumento -nv, indicando que ele não quer que o log seja enviado ao console, em caso negativo, ou seja, quando este argumento não for especificado, iremos criar um “handler”, ou seja, uma função que está atrelada ao “logger”, estamos utilizando o nível de log como INFO e o formato padrão que definimos anteriormente.

Depois dessa primeira verificação, vamos verificar se o usuário indicou, através do argumento -log, se ele precisa que os logs sejam gravados em um arquivo. Em caso positivo, salvaremos todos os logs cujo nível for INFO em um arquivo chamado info.log.

Enfim, avaliaremos se o nosso usuário deseja criar um arquivo de DEBUG, ou seja, um arquivo mais detalhado para qualquer problema que possa estar ocorrendo com nosso script. Em caso afirmativo, iremos salvar todas as mensagens cujo nível é DEBUG em um arquivo chamado debug.log.

Criando a Função main()

Agora que já definimos os argumentos e os loggers , podemos utiliza-los em nossa função main().

No primeiro bloco de código desta função, definiremos a criação dos pacotes GTP Echo Request, utlizando como entrada, os IPs passados via argumento –IP.

Primeiramente, testamos se o argumento IP não é nulo, logo após isso utilizamos o método split para que possamos transformar a string de IPs em uma lista.

Enviamos ao logger em nível INFO, os IPs passados como argumento, enfim criamos os pacotes GTP Echo Requests, percebam como é fácil criar esta lista de pacotes. A ferramenta scapy ao receber um argumento do tipo lista, já cria uma lista de pacotes com os diferentes IPs contidos naquela lista.

Caso o nível DEBUG tenha sido ativado pelo usuário, nós enviamos ao logger com nível DEBUG a lista de pacotes gerados pelo scapy.

Por fim, caso o usuário não tenha especificado qualquer valor no argumento –IP, iremos enviar um erro ao logger e terminar nosso script.

Enviando e Recebendo pacotes GTP Echo Request/Response

Quando desenhamos os argumentos que poderiam ser utilizados pelos nossos usuários, definimos que o script poderia enviar e receber pacotes em loop ou não, portanto para que possamos enviar e receber os pacotes, a primeira varíavel que precisamos validar é exatamente essa.

Verificando o argumento –loop

O bloco que se segue à esta validação, será exatamente igual, a única diferença é que quando o argumento –loop é avaliado como verdadeiro, inserimos um loop que roda para sempre, ou até que um comando de parada seja executado, tal como um CTRL+C.

O que ainda não paramos para fazer é uma função que valide a resposta da interface GTP ao nosso pacote GTP Echo Request. Portanto, antes de continuarmos desenvolvendo o bloco de envio e recebimento, vamos criar a função validate_response()

Criando a função validate_response()

O protocolo GTP é formado por elementos de informação, que denominamos de IE (Information Elements), o elemento de informação da mensagem GTP Echo Response que nos interessa é chamado de Restart Counter, ou seja, toda vez que ocorre uma reinicialização desta interface, este número é incrementado.

Portanto, para que possamos verificar a disponibilidade de uma certa interface, podemos apenas verificar se este elemento de informação foi modificado ou não.

Nossa função precisa receber apenas o pacote GTP Echo Response que a interface GTP monitorada nos enviou.

Como podem ver pelo código da nossa função, recebemos o pacote GTP Echo Response como parâmetro e tentamos extrair o elemento de informação Restart Counter, em caso de sucesso, verificamos se já existia um valor para esse elemento previamente armazenado no nosso objeto restart_counter_dict, caso esse valor seja igual ao valor atual, retornamos o valor True da função, ou seja, a interface GTP continua disponível desde o último monitoramento, caso o valor seja diferente, retornamos o valor False, ou seja, em algum momento desde o último monitoramento aquela interface ficou fora de serviço.

Aqui, gostaria de encorajá-los a estudarem esta função e entenderem a utilização dos blocos de tratamento de exceção, try , except e também os tipos de mensagem que estamos enviando ao nosso logger.

É legal perceber que nosso objeto de armazenamento de elementos de informação é um dicionário cuja chave é o IP da interface monitorada.

Enviando e Recebendo… Finalmente

Para enviar e receber pacotes, como vimos em nosso tutorial, utilizaremos a função sr(). Estabelecemos que o tempo máximo de espera de resposta de um pacote é de 5s, após este tempo esgotado, em caso de não haver resposta iremos enviar ao nosso logger uma mensagem com nível de WARNING indicando que aquela interface GTP está fora de serviço.

Para as respostas que recebermos, iremos validar utilizando a função que criamos anteriormente e iremos enviar ao nosso logger as mensagens de acordo com a saída da função validate_response().

Enfim, respeitando a especificação do protocolo, iremos esperar 60s para tomar qualquer outra ação, como por exemplo, reenviar os pacotes de monitoramente caso o argumento –loop tenha sido avaliado como verdadeiro.

Conclusão

Como vimos no tutorial scapy, podemos expandir o scapy de diversas maneiras e utilizá-lo para nos ajudar em nosso trabalho ou apenas em nossos estudos.

O Scapy é uma ferramenta extremamente poderosa para prototipagem de protocolos e ferramentas de rede. Como podemos ver, uma ferramenta de monitoramente de protocolo GTP foi escrita em meras 157 linhas!