Arquivo de etiquetas: ebpf

Recompilando o kernel na Raspberry Pi 4

Adquiri a Raspberry Pi 4 com o objetivo de realizar alguns experimentos com eBPF/XDP para filtragem de pacotes e este modelo em específico por ter uma interface Ethernet Gigabit disponível.

O Problema

O Raspberry Pi 4 vem nativo com o Kernel 4.19.75-v7l+ e um primeiro passo é checar se os módulos que eu preciso, relativos ao eBPF/XDP, estão habilitados no kernel.

Isto é feito verificando-se o arquivo config:

$sudo modprobe configs
$ zgrep -E "(BPF|XDP)" /proc/config.gz

CONFIG_BPF=y
# CONFIG_BPF_SYSCALL is not set
CONFIG_NETFILTER_XT_MATCH_BPF=m
# CONFIG_BPFILTER is not set
# CONFIG_NET_CLS_BPF is not set
# CONFIG_NET_ACT_BPF is not set
# CONFIG_BPF_JIT is not set
CONFIG_HAVE_EBPF_JIT=y
# CONFIG_NBPFAXI_DMA is not set
# CONFIG_TEST_BPF is not set

O comando acima já permitiu verificar que alguns módulos do kernel relativos ao eBPF não estão ativos. O módulo do XDP nem apareceu na listagem.

Para ativar esses módulos no kernel é necessario realizar uma recompilação do kernel, objetivo desse texto.

Obtendo o kernel

A fonte oficial do Raspberry sobre recompilação do Kernel (em inglês chamado de build) demonstra um passo a passo que vou realizar neste texto e mostra também a possibilidade de realizar uma compilação cruzada (cross-compiling).

A compilação cruzada é muito útil quando sua plataforma embarcada não tem muita capacidade de processamento. Esta restrição em processamento pode resultar em muito tempo para compilar o kernel  ou mesmo não ter espaço suficiente para baixar o código fonte do kernel. Neste texto, farei uma compilação local.

Um primeiro passo é obter todas as ferramentas necessárias via apt:

$ sudo apt-get install git bc bison flex libssl-dev

Em seguida, deve-se baixar o código fonte do kernel no github do Raspberry Pi.

Como estou com o kernel na versão mais atual, não preciso me preocupar em qual branch do github vou pegar o kernel, o atual corresponde ao mesmo kernel que o meu (Branch: rpi-4.19.y), caso o branch atual seja uma versão mais a frente da sua e você não quer atualizar o kernel mas apenas recompilar sua versão atual, atente-se a isso.

Para quem já está acostumado ao ambiente Linux é comum pensar que todo o código do kernel deve ser baixado e compilado no diretório tradicional /usr/src/linux mas fazer isso nesse diretório envolve aspectos de segurança e por isso a boa prática é trabalhar no diretório do usuário sem privilégio root:

$ cd
$ mkdir kernel
$ cd kernel
$ git clone --depth=1 https://github.com/raspberrypi/linux

Configurando o kernel

Configurar o kernel, no nosso caso significa definir os módulos que serão compilados em conjunto com o kernel para resolver nosso problema.

Uma forma muito amigável de realizar essa tarefa é utilizar uma ferramenta chamada “menuconfig” ela apresenta uma interface gráfica que facilita a procura e escolha dos módulos mas para isso é necessário instalar a biblioteca do ncurses:

$ sudo apt-get install libncurses5-dev

Em seguida fazemos (para a Raspberry 4):

$ cd linux
$ KERNEL=kernel7l
$ make bcm2711_defconfig

E finalmente rodamos o menuconfig para configurar os módulos que desejo para essa recompilação:

$ make menuconfig

A navegação no menuconfig e intuitiva e possui as instruções logo no início (setas, barra de espaco, enter, teclas Y/N/M/*), uma dica legal no uso do menuconfig é que você pode pressionar a tecla / que irá abrir uma caixa de busca e assim você pode localizar onde estão os módulos que você deseja ativar.

Por exemplo, digitei xdp na busca e confirmei que o XDP_SOCKETS está em Networking Support -> Network options, ele também mostrou que para ativar esse módulo preciso (“depends on”) ativar o suporte a Network (NET) e a BPF_SYSCALL.

Dentro do menuconfig, faça a seguinte navegação para configurar o XDP/eBPF:

1) General setup <ENTER>
1.1) Enable bpf() system call <SPACE>
1.2) Permanently enable BPF JIT and remove BPF interpreter <SPACE>
1.3) Selecione <SAVE>

O SAVE irá gravar as modificações no arquivo .config

Em seguida, volte para o início do menuconfig e selecione:

1) Networking support <ENTER>
2) Networking options <ENTER>
2.1) XDP sockets <SPACE>
2.2) BPF based packet filtering framework <SPACE>
2.3) enable BPF Just In Time compiler <SPACE>
1.3) Selecione <SAVE>

Você ainda pode teclar / para abrir o menu de pesquisa do menuconfig e buscar por bpf para verificar os módulos que não estão selecionados (=n) e seguir o Location para incluí-los no objetivo da recompilação do kernel.

Após ativar os módulos referentes ao BPF e ao XDP, você pode sair do menuconfig… Como confirmação das modificações você pode executar os seguintes comandos:

$ cat .config | grep XDP
CONFIG_XDP_SOCKETS=y
$ cat .config | grep BPF
CONFIG_CGROUP_BPF=y
CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
CONFIG_BPF_JIT_ALWAYS_ON=y
CONFIG_NETFILTER_XT_MATCH_BPF=m
CONFIG_BPFILTER=y
CONFIG_BPFILTER_UMH=y
CONFIG_NET_CLS_BPF=m
CONFIG_NET_ACT_BPF=m
CONFIG_BPF_JIT=y
CONFIG_BPF_STREAM_PARSER=y
CONFIG_LWTUNNEL_BPF=y
CONFIG_HAVE_EBPF_JIT=y
# CONFIG_BPF_LIRC_MODE2 is not set
# CONFIG_NBPFAXI_DMA is not set
CONFIG_BPF_EVENTS=y
CONFIG_TEST_BPF=m

Recompilando o kernel

Com o arquivo .config montado corretamente via menuconfig podemos seguir com a recompilação do kernel (build):

$ make -j4 zImage modules dtbs
$ sudo make modules_install
$ sudo cp arch/arm/boot/dts/*.dtb /boot/
$ sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/
$ sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/
$ sudo cp arch/arm/boot/zImage /boot/$KERNEL.img

Detalhe que o argumento -j4 do make permite que a compilação utilize os quatro núcleos do processador da Raspberry Pi 4 acelerando este processo (vale também para a RPi 2 e 3).

Este processo leva um tempo razoável de execução e também aumenta consideravelmente a temperatura da Raspberry Pi, por isso e recomendável deixar a placa em um ambiente propício e se possível com uma ventoinha.

Verificando a recompilação

Conforme inicialmente apresentado como problema, esta etapa busca demonstrar o funcionamento do eBPF/XDP após recompilação do kernel, para isso farei uso de um script de exemplo disponível em meu github:

$ git clone https://github.com/gubertoli/ids_ml.git
$ cd ids_ml/examples/bpf
$ sudo apt-get install clang-7
$ make

Carregando na interface eth0:

$ sudo ip -force link set dev eth0 xdp obj portfilter.o sec filter

Verificando:

$ ip link show eth0

Removendo:

$ sudo ip link set dev eth0 xdp off