Manual
do
Maker
.
com
Fazer o boot do Raspberry pela rede me trouxe saudades.
Em 2005 trabalhava como sysadmin em uma empresa espanhola, mas outras tarefas me eram delegadas. Ajudar a equipe de suporte era uma delas, quando não tinham uma solução para um problema. Na época, instalações de sistemas em mais de 3.000 computadores eram feitas periodicamente utilizando o Norton Ghost com boot pelo disquete. Então, dois operadores eram necessários para liberar a gravação do sistema, pois o servidor ficava em um andar e os computadores a receber a imagem, em diversos andares. Como achei o processo muito burro, idealizei o Phantom; um sistema que iniciou de uma remasterização do knoppix, passou aos moldes de embarcado (com a ajuda do mestre Marcelo Barros) e finalizou seus dias (lá pra 2011) com 14MB e uma interface gráfica feita em Qt. Era um grande sistema, que contava com boot por CD, pendrive, HD externo e boot pela rede. Tinha muitas outras características, mas o artigo não é sobre ele.
Primeiro, o boot pela rede só é suportado via cabo, utilizando PXE. A ROM procede inicializando o dispositivo ethernet, enviando uma requisição DHCP e, após receber a resposta, segue com o processo advindo do DHCP, que define os caminhos a seguir. Vou deixar claro no decorrer do artigo.
Verifique previamente se já não está suportado por padrão em seu Raspberry, assim não será necessário sequer fazer a modificação a seguir.
Pra começar, precisamos fazer a primeira configuração da OTP. Para isso, adicionamos ao final do arquivo /boot/config.txt o parâmetro program_usb_boot_mode=1 . Depois de um boot do sistema com essa definição, podemos ler o OTP com o vcgencmd (comando do qual discorri amplamente nesse outro artigo).
vcgencmd otp_dump|egrep '^17:'
O resultado deve ser esse:
Agora já pode desligar o sistema e remover o SD.
Antes de religar o Raspberry, conecte o cabo ethernet. Quando ligado, deve levar um tempo aproximado de 10 segundos para haver manifestação na rede.
Pode ser necessário em algum momento depurar a comunicação para ver como está acontecendo o handshake, ainda que seja apenas para fins didáticos. Nesse caso, recomendo a leitura desse tutorial (muito bem elaborado, por sinal). De qualquer modo, será necessário analisar se está ocorrendo a comunicação, portanto, instale o programa tcpdump:
sudo su
apt-get update
apt-get install -y tcpdump
Um comando inicial poderia ser:
tcpdump -i eth0 -vvv -XX -s9182 port bootpc
Se estiver usando Ubuntu com o novo esquema (podre) de nome de interface, deve ser algo como enp2s0 no lugar de eth0. Para confirmar, preceda o comando anterior identificando sua interface:
ifconfig
Se não tiver o comando ifconfig em sua distribuição Linux (outra mudança podre e inútil), tem duas opções: instale o pacote net-toolspara ter o comando ifconfig, ou use o comando ip. No primeiro caso:
sudo apt-get update
sudo apt-get install -y net-tools
E no caso do comando ip:
ip addr show
A saída será numerada e, tirando a interface de loopback (lo) e a WiFi (wlp3s0, wlan0 ou algo do tipo), deve sobrar a interface ethernet.
Usando o comando tcpdump citado mais acima, o resultado deve ser algo como:
Cada Raspberry tem um serial único, que pode ser pego com o comando vcgencmd ou direto no arquivo /proc/cpuinfo, gerado pelo kernel. Para pegar o (número) serial pelo arquivo cpuinfo, faça login no Raspberry e use:
cat /proc/cpuinfo | grep Serial | cut -d ' ' -f 2
E para pegar o (número) serial pelo comando vcgencmd, utilize:
vcgencmd otp_dump|egrep '^28'|cut -f2 -d:
Se quiser saber mais sobre o comando vcgencmd, recomendo o tutorial mais elaborado, exclusivamente sobre os recursos do comando vcgencmd. Se quiser saber em detalhes o significado dos bits do OTP, recomendo a documentação oficial do Raspberry.
Enfim, separe o valor do serial, pois utilizaremos mais adiante. Também vamos precisar do MAC address. Tendo identificado o nome da interface, use o comando a seguir. No Raspberry a interface ainda está com o nome eth0, então:
ifconfig eth0|egrep ether|awk '{print $2}'
O resultado deve ser algo como:
Agora podemos iniciar o processo de configuração.
Antigamente a única opção era configurar um servidor DHCP e um PXE para fazer a transação com o TFTP. Na época atual temos uma opção que poupa um monte de trabalho, da qual discorro mais adiante.
Nesse primeiro momento, vamos focar na instalação dos pacotes:
sudo apt-get install -y nfs-kernel-server dnsmasq kpartx
O Raspberry não tem uma BIOS ou algo do tipo. Quando ligada, simplesmente busca o ponteiro para inicialização em um ou mais lugares, dependendo da versão da placa. Primeiramente, busca pelo inicializador em /boot, no micro SD. No caso da Raspberry Pi 3, busca pelo sistema na rede, caso o micro SD não esteja presente. O que precisamos fazer é disponibilizar o sistema na rede, e isso requer dois caminhos distintos.
Teremos um serviço TFTP rodando no computador servidor. A partir dele será carregado o bootloader no Raspberry que, previamente à carga do sistema raiz, deverá carregar o kernel. Para tal, criamos um diretório que conterá tudo que está em /boot de um sistema do Raspberry. Por convenção, o diretório é criado em /tftpboot. Pode ser em qualquer lugar que desejar, desde que adeque os arquivos de configuração dos serviços.
Para copiar o conteúdo da partição de boot do Raspberry existem várias formas. Particularmente, tomei o caminho mais rápido.
Tire o SD do Raspberry e, em outro Linux, faça a cópia da partição:
sudo mkdir /tftpboot
sudo chown 777 /tftpboot
cp -a /media/<usuario>/bootfs/* /tftpboot
Se quiser ter imagens diferentes para diferentes computadores (no caso, Raspberry), crie o diretório /tftpboot/
Se não conhece, o NFS (Network File System) é um sistema de arquivos em rede. Com ele, podemos fazer compartilhamento de conteúdo como se fosse um compartilhamento SAMBA (ou compartilhamento Windows, se preferir referenciar-se a ele).
Criando um compartilhamento NFS, poderemos colocar o sistema raiz lá dentro e assim, permitir que o Raspberry carregue a raiz como se estivesse lendo de /dev/mmcblk0p2.
Para tal, crie o diretório /nfs/client1e copie a partição raiz para lá.
sudo su
mkdir -p /nfs/client1
chmod -R 777 /nfs
apt-get update
apt-get install rsync
rsync -xa --progress /media/<usuario>/rootfs/* /nfs/client1/
Garanta que não será servidor IP por DHCP na interface interna. Crie os seguintes arquivos:
Coloque esse conteúdo:
[Match]
Name=enp2s0
[Network]
DHCP=no
Repare que aqui o arquivo tem o nome estético da interface ethernet do computador servidor. Seu conteúdo:
[Match]
Name=enp2s0
[Network]
Address=10.42.0.211/24
DNS=10.42.0.211
[Route]
Gateway=192.168.1.1
Esse gateway nem pertence à mesma rede, mas está aí por estar. O gateway é a porta de saída de uma rede para outra, logo, o IP do gateway haveria de ser o IP da interface que está servindo o boot, exceto se o Raspberry estiver conectado a um switch, que pode lhe prover outro caminho na LAN para que ele tenha uma rota de saída da rede 10.42.0.0/24 para outra rede da LAN ou para a Internet. O caso é que não me importei de fornecer acesso à Internet, pois sendo meu notebook o servidor tftp, também haveria de configurar forwarding e mascaramento. Não é complicado, só não era o intuito, mas se desejar fazer esse roteamento, dando acesso à Internet para o Raspberry que carregou o sistema pelo TFTP, basta ler o artigo de firewall doméstico que escrevi há um bom tempo. Dadas as explicações, sigamos.
Supondo que esteja configurando a rede para navegar, também será necessário especificar um servidor DNS. Tem duas formas de oferecer o recurso, mas vou deixar a cereja do bolo para o final. Por enquanto, considere preencher esse arquivo com o IP do gateway (supondo então 10.42.0.211):
[Resolve]
DNS=10.42.0.211
Agora é hora de por os serviços para rodar, mas vamos instalar uns pacotes mais.
sudo su
systemctl enable systemd-networkd
reboot
#depois....
sudo su
apt-get install -y dnsmasq
systemctl enable dnsmasq
tcpdump -i enp2s0 port bootpc -vvv -XX -s9182
Aqui eu fiz o seguinte; configurei a estrela desse artigo - o dnsmasq - depois observei a interface de rede, fazendo um boot normal com o Raspberry e configurando a interface ethernet dele por DHCP para ver se o dnsmasq estava cumprindo seu papel. Vou dedicar um artigo exclusivo ao dnsmasq porque ele é demais! Mas por enquanto, vamos nos ater à configuração sem entrar nos detalhes técnicos do serviço.
Edite o arquivo /etc/dsnmasq.d/dnsmasq.conf e deixe seu conteúdo assim:
port=0
dhcp-range=10.42.0.10,10.42.0.12,12h
dhcp-option=3,10.42.0.211
dhcp-option=6,10.42.0.211
log-dhcp
enable-tftp
tftp-root=/tftpboot
pxe-service=0,"RPI Manual do Maker"
Mas não vamos deixar de saber o que significa essa configuração, certo?
Existe um conjunto consideravelmente grande de regras nesses protocolos, cujas definições podem ser vistas aqui. No parâmetro dhcp-option (definido duas vezes), na primeira dizemos que o gateway é o 10.42.0.211. O código que diz se tratar do gateway é o que precede a vírgula: 3.
Posteriormente dizemos que a resolução de nome vem do mesmo endereço IP, cujo código que diz se tratar disso é o 6.
O dnsmasq, entre outras coisas, é servidor DHCP. Configuramos ele para distribuir IPs entre 10.42.0.10 e 10.42.0.12 com "lease time" de 12h. Isto é, ele não renovará o IP por 12 horas.
Hora de reiniciar o serviço para que ele assuma as configurações:
sudo su
systemctl enable dnsmasq.service
systemctl restart dnsmasq.service
Não podemos nos esquecer de configurar o servidor NFS, senão não tem boot do Raspberry pela rede. Esse é bastante simples:
sudo su
apt-get install -y nfs-kernel-server
echo "/nfs/client1 *(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports
echo "/tftpboot *(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports
systemctl enable rpcbind
systemctl restart rpcbind
systemctl enable nfs-kernel-server
systemctl restart nfs-kernel-server
Não adianta nada estar servindo um sistema para o boot do Raspberry pela rede se ele não souber de onde ler o sistema raiz. Essa informação não é papel do PXE, BOOTP ou DHCP. Isso já está por conta do sistema operacional, portanto, devemos configurar o sistema raiz que está no servidor para que, após o carregamento do kernel, ele possa saber onde está o restante do sistema operacional. E de onde o sistema operacional lê essa informação? - Fácil, hum? Do arquivo /boot/cmdline.txt. Nesse caso, devemos editar o arquivo /tftpboot/boot/cmdline.txt e deixá-lo assim:
console=serial0,115200 console=tty1 root=/dev/nfs nfsroot=10.42.0.211:/nfs/client1,vers=4.1,proto=tcp rw ip=dhcp rootwait elevator=deadline
Lembre-se de acertar o IP. Além disso, o boot do Raspberry pela rede será lido das definições de seus arquivos de configuração de sistema, então se estiver lendo de /tftboot/ ou /tftpboot/
Um último ponto a considerar é a tabela de partições. Quando o sistema operacional inicia, o sistema de arquivos é montado conforme as definições que se encontram no arquivo /etc/fstab. Se estiver apontando para o UUID ou para o dispositivo de bloco diretamente, o sistema não iniciará. Desse modo, antes de fazermos qualquer teste, devemos modificar a tabela de partições no arquivo /etc/fstab. Lembre-se que, no servidor o sistema raiz ficou hospedado em /nfs/client1. Edite o arquivo /nfs/client1/etc/fstab e deixe-o assim:
proc /proc proc defaults 0 0
# a swapfile is not a swap partition, no line here
# use dphys-swapfile swap[on|off] for that
10.42.0.211:/tftpboot /boot nfs defaults,vers=4.1,proto=tcp 0 0
Mais uma vez, atente-se ao IP. Um erro bobo poderá lhe causar uma bela dor de cabeça.
Só para garantir que todos os serviços serão reiniciados, talvez seja desejável reiniciar o sistema do servidor. Ao voltar, faremos uso do comando anunciado precocemente. Não se esqueça de configurar o IP 10.42.0.211 (ou a rede que disponibilizou para essa tarefa, seja uma subnet da classe A 10.0.0.0/24, 172.16.0.0/14 da classe B ou alguma rede da classe C, desde que não conflite com a rede que está utilizando para ler esse artigo). No terminal:
tcpdump -i enp2s0 -vvv -XX -s9182 port bootpc or port 69
Quando iniciar o boot do Raspberry pela rede, deverá ver informações assim:
Vou explicar algumas coisas dessa mensagem, mas não deixe de ler o artigo sobre tcpdump para ter um pouco mais de intimidade.
O início do handshake do boot do Raspberry pela rede é feito na camada de enlace de dados, onde o MAC se anuncia, esperando que um servidor DHCP lhe ofereça um IP, para então subir para a layer 3 (camada de rede). Esse anúncio pode ser visto bem no começo da segunda linha em 0.0.0.0.bootpc > 255.255.255.255.bootps, onde o boot client faz um broadcast em busca de um boot server, anunciando seu MAC. Essa não é a primeira mensagem, é a dissolução. A primeira mensagem tem esse formato:
Mas isso é só um anúncio padrão, que vai acontecer de qualquer modo. Ele pode ser útil para saber se seu Raspberry está com o suporte a bootpc, mas se seguiu o procedimento inicial para verificar o OTP, já deverá saber a resposta antes de chegar nesse ponto do artigo. Continuando da mensagem anterior, temos o anúncio (ainda da camada de enlace) que segue com [udp sum ok], sendo um bom sinal de que houve um handshake adequado. O MAC é passado mais uma vez e então outras características são informadas, mas o que importará nesse momento é ver que Your-IP e Server-IP estão preenchidos com os IPs configurados. Daí segue uma série de outros parâmetros, dos quais podemos contemplar as opções citadas anteriormente; 3 e 6, para gateway e DNS, respectivamente.
O texto é longo, mas o processo é curto. Entendendo-o, na segunda vez será rápido!
Para o Raspberry Pi 4 será necessário algumas modificações, então não vou escrever a respeito agora porque o importante aqui é focar no servidor.
E se pretende comprar Raspberry, ficam como sugestões os parceiros do blog:
Saravati - com diversas opções de Raspberry.
CurtoCircuito - para Raspberry Pi 3.
MASUGUX - Também com diversas opções de Raspberry.
Fiz um vídeo simples apenas para mostrar o sistema "vivo", enquanto narro com uma bela enxaqueca, então, releve. Veja em nosso canal DobitaobyteBrasil, no Youtube. Inscreva-se e clique no sininho para receber notificações e, deixe seu like!
Espero que tenha gostado do artigo e até a próxima!
Revisão: Ricardo Amaral de Andrade
Inscreva-se no nosso canal Manual do Maker no YouTube.
Também estamos no Instagram.
Autor do blog "Do bit Ao Byte / Manual do Maker".
Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.