NetFilter Hook em Kernel 2.6

Introdução

Netfilter é um subsistema do Kernel do Linux superiores ao 2.4. O Netfilter é o responsável pelo filtro de pacotes, NAT, firewall, redirecionamentos, entre outros. O Netfilter é muito extensível e sua documentação é muito bem feita (procure também por “Netfilter Hacking HOWTO”). O Netfilter deixa a possibilidade de uso de HOOKs no código do Kernel, fazendo com que seu uso seja muito maleável e muito adotado pela comunidade. Estes HOOKs deixam várias possibilidades, podendo servir como gatilhos para certos eventos.
O Netfilter é muito conhecido pelo seu comando de front-end, o iptables, mas não limita-se à ele. A criação de EXTENSIONs para o iptables, a criação de HOOKs no Netfilter/Kernel se tornam muito interessantes, tendo como limitação apenas a imaginação de cada um.
A idéia de desenvolver um HOOK partiu do princípio da complexidade em se contabilizar consumo por IPs e por PORTAS, utilizando os próprios recursos do sistema. Muitos vão me dizer: mas isto é possível com fulano, ciclano, etc. Normalmente os programas que executam esta ação utilizam meios mais pesados para obtê-la, utilizando de bibliotecas como a libpcap (sniffer), entre outras formas, que acabam consumindo uma certa memória.
Desta maneira surgiu a idéia de desenvolver estes módulos em cima do próprio IPTABLES e minha experiência anterior (IPCHAINS) forçava a me atentar ao fato de que certos contadores eu poderia encontrar no /proc. Ledo engano: o iptables não armazena muita coisa interessante por lá.
Mas a melhor idéia veio realmente quando eu me deparei com o Fedora Core 4, que vinha com o SELinux e que habilitava um Netfilter HOOK sobre o sistema. Algumas pesquisas e pronto: cá estava eu com uma fonte interessante de informações, mas que não me serviam, pois elas existem muito mais para Kernel 2.4. Como fazer então com Kernel 2.6?

Constantes

Hooks IPv4 disponíveis:

  • NF_POST_ROUTING: Depois do processamento de saída;
  • NF_LOCAL_OUT: Para pacotes vindos de endereços locais no seu caminho de saída;
  • NF_IP_FORWARD: Se o pacote está destinado a outra interface;
  • NF_IP_LOCAL_IN: After routing decisions if packet is for this host;
  • NF_IP_PRE_ROUTING: Antes dos testes, depois das decisões de rota.

O Hook NF_IP_PRE_ROUTING é chamado de primeiro HOOK após o pacote ser recebido. Este é o hook apresentado aqui e que será utilizado. Sim, outros Hooks também possuem seus usos, mas iremos nos focar mais em NF_IP_PRE_ROUTING.

Códigos de retorno do Netfilter:

  • NF_DROP: Descarta o pacote;
  • NF_ACCEPT: Aceita o pacote;
  • NF_STOLEN: “Esquece” o pacote para processamento e processa internamente;
  • NF_QUEUE: Enfileira pacotes, deixando a possibilidade para manipulação em User Space;
  • NF_REPEAT: Chame este processo novamente.

O retorno NF_STOLEN é interessante porque diz ao Netfilter para “esquecer” o pacote. Isto quer dizer que o Netfilter deve deixar o processamento deste pacote a cargo deste hook e que o Netfilter não deverá fazer mais nada com ele.

Estruturas

Estrutura HOOK:

struct nf_hook_ops
{
struct list_head list;
 
 /* Campos a serem preenchidos daqui para baixo. */
nf_hookfn *hook;
int pf;
int hooknum;
/* Hooks são ordenados em ordem ascendente de prioridade. */
int priority;
};

Função básica de hook:

/* Esta é a função hook */
unsigned int hook_func(unsigned int hooknum,
struct sk_buff **skb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
return NF_DROP;  /* Descarta todos os pacotes */
}

Para registrar um hook:

/*
Esta é a estrutura que será usada para registrar o hook
É aconselhável que seja declarada como GLOBAL
*/
static struct nf_hook_ops nfho;

Dados do objeto de Hook:

/*
Para habilitar um HOOK, utilize o procedimento abaixo
(de preferência na inicialização do módulo
*/
nfho.hook     = hook_func;         /* Handler function */
nfho.hooknum  = NF_IP_PRE_ROUTING; /* First hook for IPv4 */
nfho.pf       = PF_INET;
nfho.priority = NF_IP_PRI_FIRST;   /* Make our function first */

Procedimento para registro:

nf_register_hook(&nfho);

Procedimento para remoção:

nf_unregister_hook(&nfho);

Exemplo:

#include <linux/config.h>
#include <linux/module.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
static struct nf_hook_ops nfho;

unsigned int hook_func (
unsigned int hooknum,
struct sk_buff **pskb,
const struct net_device *indev,
const struct net_device *outdev,
int (*okfn)(struct sk_buff *)
)
{
// Nega todos os pacotes
return NF_DROP;
}

static int __init nfhook_init(void)
{
nfho.hook     = hook_func;         /* função a ser usada pelo hook*/
nfho.hooknum  = NF_IP_PRE_ROUTING; /* IPv4 Prerouting */
nfho.pf       = PF_INET;
nfho.priority = NF_IP_PRI_FIRST;   /* Adiciona como primeiro hook da lista */

nf_register_hook(&nfho);

return 0;
}

static void __exit nfhook_exit(void)
{
nf_unregister_hook(&nfho);
}

module_init(nfhook_init);
module_exit(nfhook_exit);

MODULE_LICENSE("GPL");

 

Exemplo aprimorado

Tive que juntar vários pedaços de documentações para conseguir montar um exemplo conciso de como montar um módulo para kernel 2.6 (que diferem em muito dos módulos para kernel 2.4) e que fosse capaz de utilizar o netfilter.

Este exemplo (central do tutorial) demonstra como gerar pseudo-arquivos no pseudo-diretório /proc (imagem do kernel).

Basicamente o exemplo é composto de 2 arquivos:

  • Makefile;
  • nfhook.c.

Makefile:

obj-m := nfhook.o

KDIR := /lib/modules/$(shell uname -r)/build
PWD  := $(shell pwd)

default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

nfhook.c:

/*
Kernel Module for Kernel > 2.6 - Netfilter Hook
Autor: Marcel Bueno
Email: marcel#bueno.com.br
Site : http://marcel.bueno.com.br

Referencias e Créditos:

http://uqconnect.net/~zzoklan/documents/netfilter.html


http://www.captain.at/programming/kernel-2.6/


http://www.netfilter.org/documentation/tutorials/lw-2000/tut-6.html

*/

#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/ip.h>
#include <asm/uaccess.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>

// Controle de versão
static char *version = "0.0.1";

// Objeto de estrutura do nosso hook
static struct nf_hook_ops nfho;

// Função usada pelo HOOK do Netfilter (veja mais para frente)
unsigned int hook_func (
  unsigned int hooknum,
  struct sk_buff **pskb,
  const struct net_device *indev,
  const struct net_device *outdev,
  int (*okfn)(struct sk_buff *)
)
{
  // Só vamos adicionar uma linha no message-log com o código do hook
  // (para cada pacote), tamanho e de que placa veio para exemplificar...
  printk("Nfhook %d Length %d Source %s ... ", hooknum, (*pskb)->len, indev->name );

  // Neste ponto, poderíamos trabalhar um pequeno firewall, com IFs, CASES e afins,
  // Gravar um contador, separar IPs em arrays, etc.
  // Sejam criativos ;-) 
  return NF_ACCEPT;

  // Os tipos de retornos possíveis podem ser vistos logo acima, neste tutorial
}

// Mostra algo quando há leitura do arquivo no /proc
static int show_stats(char *buffer, char **start, off_t offset, int length)
{
  int size;

  // buffer é nossa variável de retorno (o arquivo)
  // Poderíamos escrever qualquer coisa aqui, desde
  // contadores simples, até sofisticados arrays
  //
  // Utilize cat /proc/nfhook/stats para visualizar o resultado
  //
  size = sprintf(buffer, "NFHOOK %s ", version);
  *start = buffer + offset;
  size -= offset;

  return (size > length) ? length : (size > 0) ? size : 0;
}

// Inicia módulo no kernel (insmod)
static int __init nfhook_init(void)
{
  struct proc_dir_entry *proc_nfhook;
  struct proc_dir_entry *proc_nfhook_stats;

  // Preenche os dados para a estrutura de HOOK
  nfho.hook     = hook_func;         /* função a ser usada pelo hook*/
  nfho.hooknum  = NF_IP_PRE_ROUTING; /* IPv4 Prerouting */
  nfho.pf       = PF_INET;
  nfho.priority = NF_IP_PRI_FIRST;   /* Adiciona como primeiro hook da lista */

  // Registrando o processo de HOOK
  nf_register_hook(&nfho);

  // Cria os apontamentos em /proc/nfhook
  proc_nfhook = proc_mkdir("nfhook", 0);

  if (!proc_nfhook)
  {
    printk (KERN_ERR "cannot create /proc/nfhook ");
    return -ENOMEM;
  }

  // Pseudo arquivo... quando executar um CAT, utilizar o procedimento show_stats
  proc_nfhook_stats = create_proc_info_entry("nfhook/stats", 0, 0, show_stats);

  if (!proc_nfhook_stats)
  {
    printk (KERN_ERR "cannot create /proc/nfhook/stats ");
    remove_proc_entry("nfhook", 0);
    return -ENOMEM;
  }

  // Você pode também montar um procedimento ao gravar no arquivo /proc/nfhook/stats !
  // proc_nfhook_stats->write_proc = add_to_nfhook;  (Não implementei)

  return 0;
}

// Ao remover o módulo (rmmod)
static void __exit nfhook_exit(void)
{
  /* Remove os pontos de acesso ao /proc */
  remove_proc_entry("nfhook/stats", 0);
  remove_proc_entry("nfhook", 0);

  /* Retira o HOOK da memória */
  nf_unregister_hook(&nfho);
}

module_init(nfhook_init);
module_exit(nfhook_exit);

MODULE_LICENSE("GPL");

Existindo estes 2 arquivos, simplesmente compile com o comando:

# make

Se tudo correr bem, será gerado um arquivo chamado “nfhook.ko”. Para inserí-lo como módulo no kernel:

# insmod ./nfhook.ko

Aplicações

Como disse anteriormente, este tipo de programação só se limita a sua imaginação. Abaixo algumas possíveis utilidades:

  • Firewall leve, compilados;
  • Firewall com alarme;
  • “Gatilho” para determinados eventos;
  • Contadores de pacotes.

E como toda tecnologia que pode ser usada para bons propósitos, também existem outros:

  • Regras do além (aquelas que não podem ser visíveis);
  • Backdoors ocultos e Rootkits;
  • Sniffers;
  • Capturadores de senhas (veja exemplo para kernel 2.4);
  • Log e decriptação de pacotes de mensagens instantâneas.

Referências e maiores estudos

Este documento não foi totalmente escrito por mim. É um resultado de várias pesquisas, ajustes e traduções.

Seguem abaixo os links relacionados:

Deixe uma resposta

O seu endereço de email não será publicado Campos obrigatórios são marcados *

Você pode usar estas tags e atributos de HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>