Discuta este tópico no fórum

Se este conteúdo te ajudou, deixe um presente!

domingo, 29 de dezembro de 2013

OpenWRT: Recuperação de desastre em vídeo

Este é mais um artigovídeo da série sobre o OpenWRT

Depois de diversas dúvidas na postagem sobre recuperação do OpenWRT, resolvi mostrar os procedimentos em vídeo. Assim ficam menos dúvidas de como apertar o botão, quando, e o que é esperado.





Até a próxima! E feliz 2014!

sábado, 24 de agosto de 2013

OpenWRT: Conectando um modem 3g no seu roteador

Este é mais um artigo da série sobre o OpenWRTDentre os diversos usos de uma USB em um roteador com OpenWRT, um dos mais interessantes é a possibilidade de utilizá-la para conectar um modem 3g.

A rede de dados móvel já possui uma cobertura bem interessante, ao menos nos grandes centros. Em alguns casos, já contamos com a disponibilidade até de redes 4g. Já não é raro a situação onde a conexão de dados móvel tem maior velocidade de transferência do que a conexão cabeada. Contudo, em geral, a latência da rede de dados móvel é bem pior.

Os modem 3g tem um caráter individualista: você conecta diretamente no seu PC e somente ele usa a conexão de dados. Claro que existe a possibilidade de compartilhar a internet através de um outro computador, mas não é nada prático. Um roteador para esta função seria o ideal. É ótimo! Na infraestrutura, você só precisa de uma tomada e pode fornecer internet para um grupo PEQUENO de pessoas. Existe alguns produtos no mercado com o slogan de "roteador 3g" ou "roteador para modem 3g". Eles são roteadores wireless com uma porta USB ou mesmo, nos casos de um modem embutido, uma entrada para SIM Card. Alguns até contam com uma porta Ethernet para aumentar as possibilidades.

Mas qual a diferença destes roteadores "3g" e o que eu tenho aqui? Ele também tem uma porta USB! Por que não funciona conectar um modem 3g nele? A diferença é somente software. Os fabricantes, por ser interessante para seu negócio, limitam as funcionalidades do software do seu roteador. Com OpenWRT, o que você vai fazer com a USB é problema seu. Não temos qualquer limitação artificial.

O primeiro passo para a utilização do modem é a instalação dos drivers e programas para seu modem. Sempre tome cuidado no uso de disco pois colocar muitas funções no seu roteador irá acabar com o seu limitado espaço em disco (que você pode resolver com uma unidade externa USB). Se precisar de mais de uma porta USB, para usá-las também para conectar HD, impressora, você pode precisar de um Hub USB. Só lembre-se que, se for usar um Hub USB, seu modem 3g pode necessitar de um Hub com alimentação externa. O roteador, normalmente, não tem potência suficiente para alimentar mais dispositivos em conjunto com um dispositivo pesado como um modem 3g. Sem o Hub  normalmente, o roteador dá conta.

Considerando que você tem espaço, os pacotes necessários são:
  • comgt
  • kmod-usb-serial
  • kmod-usb-serial-option
  • kmod-usb-serial-wwan ou kmod-usb-acm (depende do seu modem)
  • usb-modeswitch
  • usb-modeswitch-data (Atualizando 3: não necessário para versão BB ou superior)
  • luci-proto-3g (para permitir a configuração pela interface WEB)
Atualizando 4: Atualmente existe uma nova classe de modems 3g que não são CDC-ACM. Usam o padrão QMI. São outros pacotes e a configuração é ainda apenas via arquivo de configuração. Atualizarei sobre estes modens no futuro.

Para instalar, pode ser feito pela interface gráfica (sempre rodando um update antes para baixar a lista de pacotes) ou pela linha de comando:
opkg update

opkg install comgt kmod-usb-serial ...
Se estiver com dúvidas quando ao uso do kmod-usb-serial-wwan ou kmod-usb-acm, você pode instalar todos os pacotes (se tiver espaço). Se quiser descobrir qual é o correto, pode também conectá-lo em um desktop Linux e ver qual módulo é carregado (acm ou wwan). Algo como:
linuxdesktop:$ lsmod | grep acm
Vai listar se o módulo cdc-acm foi carregado.

Atualizando 3: Para a versão BB, é necessário ativar o serviço usbmode.

Instalado os módulos e programas, ao conectar o modem, você encontrará alguns dispositivos novos em /dev/. Eles podem ser /dev/ttyUSB{0,1,3} ou /dev/ttyACM. Isto indicará que seu modem foi reconhecido. Se não aparecer, tende reiniciar ou remover/reconectar o modem. Depois do dispositivo presente, basta adicionar uma nova interface. Você também poderá ver a criação destes dispositivos pelos logs na interface WEB.

Vou limitar a configuração pela interface WEB, que é mais simples e atende um público maior. Em "Rede/Interfaces", clique para adicionar uma nova interface. Escolha o nome que quiser, mas costumo seguir um padrão como wwan (wireless wan). No protocolo  escolha "UMTS/GPRS/EV-DO". Ao prosseguir, você poderá configurar os demais parâmetros.

Nesta configuração, o dispositivo do modem vai ser um dos dispositivos que apareceram com o modem (/dev/ttyUSB0 ou /dev/ttyACM). O tipo de serviço, APN, e usuário e senha depende do seu provedor mas, se não souber, pergunte para quem sabe. O PIN só é necessário se seu cartão foi protegido por senha. Se não souber o que é isso, provavelmente não precisa. Por padrão, o roteador irá conectar pelo modem assim que o roteador ligar ou quando o modem for conectado. Se quiser fazer isto manualmente, desative a opção de conexão na inicialização em "Opções Avançadas". Ainda falta a configuração da zona do firewall, que é fundamental para o funcionamento. O mais simples é simplesmente configurar esta nova interface na zona WAN. Assim, a mesma conectividade que você teria pela porta WAN, você terá na conexão 3g. Se tudo der certo, após aplicar as configurações, seu modem irá conectar em breve.

Sentiu falta de alguma coisa? Ah, não tem opção de configurar o número do telefone pela configuração do OpenWRT. :-( E, para alguns casos, isto é fundamental (Vivo). Para mudar o número de discagem, edite o arquivo /etc/chatscripts/3g.chat e mude o número do comando ATDT. Ex:
/etc/chatscripts/3g.chat:

OK      "ATDT*99#"
No roteador, você tem o editor vi. Para novatos ele é bem estranho mas fundamental saber trabalhar com ele para se virar neste mundo UNIX. Também pode copiar o arquivo, editar no desktop e copiar de volta. Só cuidado para usar um editor que não troque o "nova linha" de UNIX para Windows. Não use o notepad!

Atualizando 1: a versão Barrier Breaker (14.07) já consta com uma opção na configuração para definir o número de discagem: dialnumber. Entretanto, ela não está ainda disponível na interface Luci (WEB). Desta forma, ainda será necessário editar um arquivo de configuração (/etc/config/network) ou usar comandos uci.

Atualizando 2: a futura versão do Luci que acompanhará o OpenWRT Caos Calmer (em desenvolvimento) terá suporte a configuração do dialnumber pela interface web.

Mas ainda não funcionou! Observe as mensagens do sistema (pela interface WEB ou comando logread). Lá estará o motivo de não ter funcionado.

Se a conexão 3g é a sua "segunda conexão", você pode estar interessado também no artigo sobre balanceamento de conexões.

Até a próxima.

PS: Depois que acabar alguns projetos prioritários que estão em fase de conclusão, vou ver se gasto um tempo para adicionar a opção do número de discagem na interface WEB.

sábado, 13 de julho de 2013

OpenWRT: Cópia de arquivos de/para o roteador

Mais um artigo da série sobre o OpenWRT.

Um dos problemas comuns dos novatos no OpenWRT é como fazer a cópia de arquivos de e para o roteador. Em geral, não existe grande diferença entre copiar de um computador com Linux ou de um roteador com OpenWRT. Afinal, ele também é um Linux. Porém, pode ser um pouco diferente para usuários acostumados com o ambiente Windows.

Nativamente, o OpenWRT vem com o servidor SSH Dropbear. Tendo um servidor SSH, já podemos realizar a cópia simples de arquivos pela entrada e saída padrão. Basta que seu ambiente seja um UNIX, como um Linux, BSD ou um MacOSX. Ex:
# copiando o /etc/passwd do roteador

# para /tmp/passwd.router na máquina local
ssh root@router cat /etc/passwd | cat > /tmp/passwd.router 
# copiando o /etc/resolv.conf do computador

# para /tmp/resolv.conf.exemplo no roteador
cat /etc/resolv.conf | ssh root@router "cat > /tmp/resolv.conf.exemplo"
O primeiro "| cat" poderia ser otimido mas deixei para uniformizar os dois comandos.

Claro, dificilmente se usa este recurso para cópias. Uma alternativa mais simples é utilizar o scp. Para este, ao menos, temos uma alternativa para usuários do Windows: o winscp. A partir de ambientes UNIX como o Linux ou o Mac OSX, podemos fazer:
# copiando o /etc/passwd do roteador

# para /tmp/passwd.router na máquina local
scp root@router:/etc/passwd /tmp/passwd.router 
# copiando o /etc/resolv.conf do computador

# para /tmp/resolv.conf.exemplo no roteador
scp /etc/resolv.conf root@router:/tmp/resolv.conf.exempl 
Se a ideia é copiar vários arquivos, você pode usar a opção "-r". Alternativamente a opção '-r', você pode utilizar a primeira solução mas com o comando tar. O tar cria arquivos em formato usado para fitas mas, atualmente, é mais usado como um arquivador. Em conjunto com um compactador de arquivo, ele é similar a um zip. Fica assim:
# copia o conteúdo de /overlay do 

# roteador para o diretório atual
ssh root@router tar -cz /overlay | tar -xzv
Inclusive, pode trocar o "tar -xz" por um "cat > overlay.tar.gz" para criar um tar diretamente pela rede, sem criar o arquivo no roteador. Uma ótima forma de fazer um backup de conteúdos do roteador.

Até este ponto, não era necessário instalar qualquer programa no roteador. Agora vamos as alternativas instaláveis.

Um grande software de cópia de arquivos é o rsync. É um programa de sincronização de diretórios, podendo ser ambos locais ou um deles remotos. Na maioria dos casos, eu o uso como ferramenta de cópia no lugar do scp pois você pode "continuar" a cópia de um diretório com grande números de arquivos e, usando as opções corretas, continuar a cópia de um arquivo grande. Para utilizá-lo no OpenWRT, é necessário instalá-lo.
opkg install rsync
E copie de forma similar ao scp
rsync -av root@router:/overlay /tmp/overlay
Outra opção é usar o SFTP, um FTP sobre o SSH. Pelo fato do servidor SSH do OpenWRT ser desenvolvido para ambientes embarcados, alguns recursos foram suprimidos. Como temos o scp, o sftp ficou de fora. Contudo, ele está preparado para utilizar o servidor sftp do openssh. Isto vai exigir, no mínimo uns 700 Kbytes de espaço de disco livre:
opkg install openssh-sftp-server
Depois de instalar, você já pode usá-lo. Não precisa reiniciar o roteador ou mesmo o dropbear. A vantagem do sftp é poder utilizar navegadores gráficos clássicos como o filezilla ou o suporte nativo dos gerenciadores de arquivos no Linux (gnome nautilus e kde dolphin) para o protocolo sftp://root@router/.

Um ponto interessante de todas as opções que foram listadas aqui é que todas operam sobre o canal do SSH e, portanto, utilizam somente a porta do serviço SSH (22). Tudo criptografado e seguro, desde que a sua senha esteja segura. Se quiser fazer o acesso de fora da rede interna, basta liberar a porta 22 do firewall do OpenWRT.

Até a próxima!

quinta-feira, 27 de junho de 2013

OpenWRT: Transforme seu roteador em uma mediabox (DLNA)

Mais um artigo da série sobre o OpenWRT.

Um dos usos mais frequentes que faço do o roteador, fora a função de "roteador", é usá-lo como fonte dos arquivos multimídias da casa. O roteador fica sempre ligado para suprir a função de "acesso internet". Como ele já vai ficar ligado de qualquer forma, nada mais prático do que utilizá-lo como servidor de mídias da casa.

Claro que não faz muito sentido utilizar o roteador como uma mediabox se não adicionar um HD externo a ele. Para colocar seus arquivos de mídia, você pode copiar por um servidor de arquivos ou mesmo baixar diretamente nele. Talvez vai faltar espaço para instalar os programas necessários e você precisará estender o disco interno.

Em geral, os dispositivos que tocam as mídias de uma mediabox recebem dados pelo protocolo DLNA. Para prover DLNA em dispositivos embarcados com recursos restritos, como é o caso de NAS e roteadores, foi desenvolvido o minidlna. Contudo, é um produto simples mas funcional que utilizo mesmo para computadores de mesa com Linux. Aparentemente ele foi renomeado para readymedia. Porém, tudo que é possível encontrar sobre este programa ainda é referenciado ao minidlna.

O minidlna supre grande parte das necessidades da maioria dos usuários. Por ser desenvolvido para ambientes limitados, não faz qualquer transcoding das mídias (seu roteador não aguentaria). Tudo é enviado como está no disco. Portanto, se algumas coisas não funcionarem no seu dispositivo, reclame da sua TV. Ele busca pela capa do vídeo/álbum e as fornece ao tocador. Também utiliza o recurso de inotify do Linux para observar novos arquivos sem a necessidade de escanear novamente todo o diretório. Um diferencial é que ele também permite o envio das legendas (SRT) de seus vídeos. Todavia, por ainda não ser uma funcionalidade padrão do DLNA, a implementação de tocadores que utilizam este recurso é limitadaEm  geral, as novas TVs já adicionaram o recurso de legendas pelo DLNA. Minha experiência com uma televisão LG é fantástica: ela toca todos os formatos que já me deparei e ainda solicita as legendas.

A instalação no OpenWRT é ligeiramente simples. Se optar por instalar a interface web de gerenciamento, basta instalar luci-app-minidlna e o resto será instalado por dependência.
opkg install luci-app-minidlna

Era para aparecer naturalmente no luci em "Serviços". Se não aparecer, como foi meu caso, force o luci recriar seus menus:
rm /tmp/luci-indexcache
Se optar por gerenciamento somente pelo terminal, instale diretamente o "minidlna". O único arquivo a ser editado é o /etc/config/minidlna.
opkg install minidlna
A principal configuração é o seu diretório de mídia (media_dir). Você pode ter um ou mais diretórios. Se adicionar mais de um, todo o conteúdo destes será apresentado para seu tocador como se estivesse no mesmo diretório. Se quiser, pode também definir o local do banco de dados em uma unidade persistente, como seu HD externo. Se deixar o padrão, ele será recriado toda vez que o roteador reiniciar.

Ative o serviço e dispare-o. Pode ser tanto pela interface web ou pelo terminal:
/etc/init.d/minidlna enable
/etc/init.d/minidlna start
Uma dica: se estiver baixando torrents direteamente no seu roteador, é bom usar o recurso que separa as pastas de arquivos incompletos dos completos. Assim, você pode configurar o minidlna para somente observar os arquivos na pasta de conteúdo completo. Outra dica: dependendo do tamanho de sua biblioteca, você pode ficar sem memória RAM. Para resolver, use memória swap.

Pronto! Ele logo será descoberto na rede pelos seus dispositivos suportados. Utilizo tanto para tocar vídeos e músicas em uma TV, em um tablet e no meu PC. Muito prático.

Até a próxima!

sábado, 18 de maio de 2013

OpenWRT: Utilizando conexões wireless para ligar duas redes - configuração

Depois de apresentar o problema do uso de uma conexão wireless para interligar dois segmentos de redes, vamos a parte de configuração. Sabendo o que está sendo feito, é uma configuração ligeiramente simples, próxima do uso de duas redes WLAN.

Neste exemplo, estou tentando conectar dois roteadores por uma rede sem fio e ainda permitir que os equipamentos conectados nas suas portas cabeadas ou na rede sem fio se comuniquem. Meus roteadores são dois Tp-Links: TL-WR 2543ND e um TL-WR740N (um patrocínio em hardware da Tp-Link Brasil seria bem vindo). WDS de fabricantes diferentes é receita para problemas. Porém, não sei se estes "fabricantes diferentes" vale também para roteadores de fabricantes diferentes mas ambos com OpenWRT. Se o fabricante da wireless for o mesmo (atheros, broadcom), a chance de funcionar é grande. Como todos os meus são Tp-Links e atheros, não tive problemas.

O primeiro passo é criar uma nova rede wireless em um dos roteadores. Optei por configurar como o "master" meu roteador principal que está conectado na internet. Como são somente 2 roteadores, não faz diferença em quem será o mestre ou cliente.

É importante conhecer a diferença entre duas coisas: o rádio e a rede wireless. O rádio é a sua "placa wireless" dentro do roteador, em geral um só. A rede são configurações para usar este rádio, podendo existir várias. Em modelos recentes existe placas dual-band independentes, que permitiria configurações próprias para cada rádio. No meu caso, e para a maioria dos mortais, tenho um roteador com um rádio (o TL-WR 2543ND trabalha em 2.5GHz ou 5GHz mas não ao mesmo tempo). Com isto, todas as redes criadas compartilharão o mesmo canal, potência e demais atributos do rádio. Vamos à configuração.

Além desta conexão wireless entre os roteadores, pretendo manter a função de AP do roteador para meus clientes wireless convencionais. Portanto, vou adicionar uma nova rede e manter a antiga. Pela interface Web, no roteador escolhido para ser o "mestre", em configuração de rede, Wifi, clique em "Adicionar". Lembre-se que a parte superior é do rádio e, se alterada, aplica-se a todas as redes wireless. Na parte inferior temos a configuração da rede wireless. Escolha o tipo "Ponto de Acesso (AP) WDS". O nome é de sua livre escolha. Como quero estender a minha rede LAN, coloquei ela na interface LAN, junto com a rede wireless antiga e as portas LAN locais. Na parte de segurança, defina a criptografia e senha. Pode ser uma senha difícil pois ela será usada apenas mais uma vez.

A configuração pela interface Web adicionará estas linhas em /etc/config/wireless:
/etc/config/wireless: 
config wifi-iface
option device 'radio0'
option mode 'ap'
option ssid 'minha_rede_wds'
option wds '1'
option network 'lan'
option encryption 'psk-mixed'
option key 'minha_senha_super_secreta'
Não temos mais configurações no roteador master. Basta agora configurar o cliente WDS no outro roteador.

O cliente é um pouco mais complicado. Primeiro você precisa desativar as funcionalidades que caracterizam um roteador. A interface WAN não será usada e pode, inclusive, ser realocada como outra porta LAN. Se mantida e não usada, não terá problemas. O firewall também não deve ter uso pois a rede LAN não é normalmente protegida. O principal que precisa ser alterado é desativar o servidor DHCP para a rede LAN e usar um IP na mesma rede mas não conflitante com o outro roteador. O DHCP usado pelos clientes conectados neste roteador será do roteador principal. Vamos a configuração da rede.

Em Rede/Interfaces, edite a rede LAN. O IP fixo é o padrão. Se o primeiro roteador mantiver o IP original (192.168.1.1), use, por exemplo, 192.168.1.2. Configure como roteador e servidor DNS o roteador principal (192.168.1.1). No final da página, existe a opção para desativar o servidor DHCP nesta rede: "Desabilita DHCP para esta interface".

A configuração da rede wireless é similar à feita no roteador master. A diferença fica apenas no modo, que deve ser "Cliente (WDS)" e na possibilidade de especificar o BSSID (endereço MAC da interface wireless do roteador). Cuidado com o BSSID, que ele pode ser diferente para a rede principal, em modo AP, e as secundárias, em nodo "AP WDS". Segurança e rede são iguais ao feito no roteador principal. A configuração no cliente fica desta forma: 
/etc/config/wireless: 
config wifi-iface
option ssid 'minha_rede_wds'
option encryption 'psk2'
option device 'radio0'
option mode 'sta'
option bssid '92:F6:52:FF:FF:FF'
option key 'minha_senha_super_secreta'
option wds '1'
option network 'lan'
Neste momento, o roteador secundário está pronto para receber clientes cabeados. Eles terão o mesmo acesso dos clientes conectados por cabo no roteador principal, inclusive acesso à internet.

Uma das características do protocolo 802.11 é que, quando disponível mais de uma rede com o mesmo nome, os clientes devem escolher a com melhor qualidade ou mesmo mudar se uma delas estiver pior. Além de permitir o uso de clientes cabeados conectados no segundo roteador, quero que os clientes wireless possam usar este segundo roteador como um repetidor wireless. Por isto, configurei uma segunda rede wireless com exatamente as mesmas configurações da interface em modo AP do roteador principal. Por compartilhar o mesmo rádio da conexão WDS, o canal ainda será o mesmo.

Pronto, agora você pode utilizar um segundo roteador como uma extensão de sua rede cabeada e repetidor wireless. Com dois AP, você terá uma cobertura sem fio muito melhor, mesmo quando estiver perambulando. Ainda, se um dos roteadores estiver próximo da sua TV, home theather, você terá um sinal de qualidade mesmo com dispositivos sem fio de baixa recepção. Ou melhor, para que comprar o adaptador sem fio extremamente caro da sua TV? Você pode usar um cabo conectado ao roteador secundário! Posso apostar que a qualidade será muito superior.

Até a próxima.

sábado, 11 de maio de 2013

OpenWRT: Configurando memória swap

Mais um artigo da série sobre o OpenWRT.

Como sempre, o limitador do que pode ser feito em um roteador com OpenWRT é o seu hardware. Quanto ao processador, não temos o que fazer mas, em geral, ele é suficiente, exceto se você quiser fazer transcoding de vídeo em tempo real (não, não dá para fazer no seu roteador). O disco é um dos problemas mais incômodos e já foi tratado em um outro post. Neste foi apresentar a alternativa para contornar a falta de memória RAM.

Se você leu o artigo sobre fazer download de torrents no roteador, pode estar enfrentando problemas com a falta de memória. O sintoma é de processos que desaparecem e avisos de "out of memory" nas mensagens do kernel (dmesg). Pessoalmente, não tive problemas com memória e torrent pois meu roteador tem 64 MB de RAM, o dobro do normalmente disponível em modelos anteriores. Porém, enfrentei a falta de memória ao utilizar um servidor DLNA, enquanto ele varria os arquivos de vídeo.

A solução para a pouca disponibilidade de memória é a mesma faz muito tempo: jogar informações menos usadas em uma memória mais lenta, como o disco. No Linux, chamados esta área em disco de memória swap, ou área de troca. Alguns também chamam de "memória virtual", mas não gosto do termo por confundir a técnica de utilizar endereçamento virtual para regiões de memória.

Para configurar a swap, em primeiro lugar, você precisa de um local para armazená-la. Ele pode ser um disco montado remotamente (NFS), uma unidade remota (NBD) ou simplesmente uma unidade de armazenamento conectada na USB. Nem pense em usar a swap na flash interna do roteador. Além de ser mais escaço que a própria memória RAM, ela não é feita para ser reescrita um grande número de vezes e, se der problema, não é facilmente substituível. Por isto, o primeiro passo é ter acesso a esta unidade externa.

A swap no Linux pode ser implementada em qualquer coisa que se assemelha, na filosofia UNIX, a um arquivo: um arquivo no sistema de arquivos, uma partição, um disco inteiro. A forma mais simples é utilizar uma partição dedicada para esta função.

Reparticione seu disco e deixe uma pequena partição para a swap. Faça com sua ferramenta de particionamento favorita (partition magic, gparted). Pode, inclusive, ser pequena, como 128 MB. Se quiser exagerar, 1 GB é muito mais do que o seu roteador um dia pensou em usar. O problema de ter muita memória swap é quando um processo se perde e começa a usar indefinidamente memória. Quanto maior a memória, mais tempo até o processo conseguir encher a memória e ser morto pelo kernel.

A partição tem que ter o identificador de uma partição swap. Em hexadecimal, é o valor 0x82. Isto é feito com as ferramentas de particionamento, mas pode também ser feito mais adiante com o fdisk, inclusive se instalado no roteador. A saída de um comando "fdisk -l" deve ter uma linha similar a em destaque abaixo:
   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1              63   123758591    61879264+   7  HPFS/NTFS/exFAT
/dev/sdb2       125853210  2928904191  1401525491    f  W95 Ext'd (LBA)
/dev/sdb3       123758592   125851647     1046528   82  Linux swap
Com a partição pronta e com a identificação correta, é o momento de "formatar" a partição. O formatar para uma partição swap é basicamente escrever uma assinatura no começo do disco. É bem rápido. Também é interessante definir o nome da partição, que pode ser usado para referenciá-la mais adiante (no exemplo, "minhaswap").
mkswap -L minhaswap /dev/sdb3
O /dev/sdb3 é o dispositivo da sua partição, como listado na saída do comando "fdisk -l". Para utilizar a swap no seu roteador, você precisará dos utilitários básicos para swap, como o mkswap. Instale o pacote swap-utils.
opkg updateopkg install swap-utils

Agora é a hora de testar a swap. Ative-a com o comando swapon:
swapon /dev/sdb3
Você poderá ver o espaço disponível pelo comando free:
# free             total         used         free       shared      buffersMem:         61700        60480         1220            0         1160-/+ buffers:              59320         2380Swap:      1046524        18884      1027640

Se a linha swap estiver zerada, não está funcionado. Caso contrário, você já estará usando sua nova swap. Contudo, esta configuração feita pelo swapon é volátil. Ao reiniciar o roteador, ela será perdida. Para ativar automaticamente, vamos usar o mesmo recurso que usamos para montagem de disco externo: o arquivo /etc/config/fstab. No arquivo, é possível configurar a montagem de uma swap. A configuração é um pouco mais simples do que partições de arquivos pois não precisa especificar onde será montada. Basta adicionar:
/etc/config/fstab:
config swap
option label "minhaswap"
option enabled 1

Note que o "label" é o mesmo que foi definido ao formatar a partição. Assim como o caso de uma partição de arquivos, também poderia referenciar o disco pela opção "device" ou "uuid". Alternativamente, existe também a opção autoswap, que ativará qualquer partição com identificação 0x82 e formatada como swap.

/etc/config/fstab: 
config global autoswap
option from_fstab 0
option anon_swap 1
Reinicie o roteador para garantir que tudo funciona após reiniciar. Observe se o espaço da swap estará disponível pelo comando free.

Pronto! Pode abusar de seu roteador que agora ele terá memoria extra, mas mais lenta, para suas tarefas.

Peraí, mas não quero mexer com partições! Tem como fazer com um arquivo simples? Tem sim. Em primeiro lugar, crie um arquivo com o tamanho da swap desejada. Use o dd para isto. O tamanho é passado pela opção "count" e é em blocos de 512. Sendo assim, por exemplo, escolha 2 par 1K, 2000 para 1M, 200000 para 100M, e assim por diante. Para uma swap de 50 MB, use:
dd if=/dev/zero of=/mnt/meudisco/swap count=100000
O arquivo /mnt/meudisco/swap deve ficar no disco externo e será, incialmente, completamente zerado. Vai demorar um pouco se for feito no roteador. Depois, você poderá usar os comandos "mkswap" e "swapon" diretamente sobre o arquivo:
mkswap /mnt/meudisco/swapswapon /mnt/meudisco/swap
O problema desta abordagem é que, por não poder usar o /etc/config/fstab, vai se perder ao reiniciar o roteador. Você precisará executar o swapon toda vez que o roteador foi ligado, ou colocar em alguns script como o "/etc/rc.local".

Até a próxima.

quarta-feira, 8 de maio de 2013

OpenWRT: Utilizando conexões wireless para ligar duas redes - conceitos


Como este post vai ficar um pouco longo, vou dividi-lo em partes. Nesta vou apresentar o problema. Nas próximas vemos as configurações.

Um dos problemas mais comuns de redes wireless é a cobertura do sinal. Vigas, vidro e diversas coisas desconhecidas podem prejudicar seu sinal. Além disto, o sinal do seu equipamento receptor pode não ser uma das melhores maravilhas. Notei isto logo que recebi minha SmartTV. A qualidade do sinal wireless é péssima. A solução para problemas de cobertura são basicamente: reposicionar seu roteador, melhorar a antena e aumentar a potência.


Também comprei um home theater que tem rede mas a wireless é paga "a parte", e muito cara. Isto é comum também nas TVs, mas não é o meu caso. Pelo preço, prefiro comprar outra coisa. Para solucionar o problema de cobertura e para substituir a necessidade de "módulo wireless", optei por adicionar um segundo roteador.

Roteador como AP


O que, em geral, temos em casa é um roteador wireless, que faz o papel de uma ponte da rede sem fio com as portas da rede LAN cabeada (função AP). A função "roteador" é composta de outras funcionalidades como NAT, DHCP, firewall, fazendo a interligação da rede LAN, cabeada ou sem fio, ao mundo externo, geralmente conectado na porta WAN. Portanto, um roteador wireless inclui as funções de um AP simples. Para usá-lo como somente um AP, basta não usar a porta WAN e desativar o DHCP (não preciso nem dizer que dois servidores DHCP ligados em uma mesma rede é receita para desastre). Isto é comum quando o roteador é fornecido pela telecom (roteador ADSL). Achei esta figura que ilustra como ficaria a conexão entre os dois roteadores (e um modem cabeado):


Fonte:https://lantechca.files.wordpress.com/2011/05/add-wireless-ap.png

Mas essa é a melhor forma de ligar os dois roteadores? Sim. E se eu ligar a LAN do roteador da esquerda na WAN do roteador da direira? Sim, pode funcionar. Se as redes LAN dos dois roteador não forem conflitantes (ex: 10.0.0.0/24 e 192.168.1.0/24), você terá conectividade. Porém, você estará fazendo duas vezes a NAT. Se a NAT é ruim para as aplicações pear-to-pear (skype, torrent, emule), duas é um desastre. Não existe técnica de transversão NAT que ajude. Além disto, computadores que estiverem conectados nas portas LAN do roteador da esquerda não irão "enxergar" os computadores conectados no roteador da direita, seja por cabo ou sem fio (mas o inverso sim). E, para os que gostam de jogar online, vai ter ainda mais latência. Ah, e nada de IPv6 no roteador da direita.

Múltiplos access-point wireless

A tecnologia sem fio 802.11 já foi pensada na possibilidade de um usuário se deslocar ao longo de um determinado local, onde o sinal é fornecido por uma série de equipamentos. O princípio é bem simples: quando existir mais de uma rede com as mesmas características (nome, segurança, senha), é função do equipamento cliente escolher o access-point que forneça o melhor sinal. Isto sem mesmo informar a desconexão ao usuário. No máximo deve ser sentido uma pequena engasgada durante a nova autenticação. Tudo isto funcionará se as estações sem fio estiverem operando como APs e compartilharem a mesma rede local.

Aproveitando, independente do OpenWRT, se for construir uma pequena rede sem fio composta de uma série de APs conectados a uma mesma rede e que usam a mesma senha, não escolha SSID diferentes para cada uma! Só vai te atrapalhar e dificultar a vida do usuário.

Roteador Wireless e AP fornecendo múltiplos access-point wireless

Agora vamos juntar os dois conceitos: roteador operando como AP e mais de uma estação fornecendo a mesma rede sem fio. Voltando a figura anterior, se o roteador "Router" fosse substituído por um roteador wireless, você teria dois provedores do sinal da sua rede sem fio. Um seria o roteador principal, operando como roteador da rede e estação wireless, e o segundo roteador wireless operando apenas como um AP. A interconexão entre os dois é feita através de um cabo convencional. Para qualidade de conexão, é a melhor situação. Os equipamentos conectados, seja por cabo ou sem fio, em ambos os roteadores irão enxergar qualquer outro equipamento conectado.

Para ajudar a melhorar o sinal da rede sem fio, os dois roteadores wireless precisam ficar afastados. Passar um cabo de rede longo pode não ser o que a maioria deseja. Inclusive eu. Não seria melhor que os dois roteadores se comunicassem pela rede sem fio? Seria ótimo! Mas tem alguns problemas ao utilizar redes sem fio para ponte. Vamos simplificar na situação onde é desejado conectar duas áreas de rede, com diversos computadores, através de uma conexão wireless.

Interligando dois setores de rede por conexão wireless

A tecnologia Ethernet cabeada e sem fio operam de maneira diferente. Tanto na cabeada quanto na sem fio, os equipamentos são identificados por um endereço físico (MAC). Por um cabo podem trafegar inúmeras conversas entre computadores diversos. Já em uma conexão sem fio, o access-point wireless somente aceitará dados oriundos do mesmo endereço MAC do cliente que fez a conexão. Isto é um problema? Não para o uso comum. Sim para o nosso caso. Vejamos a figura seguinte, onde é mostrado dois computadores conectados por cabo em equipamentos wireless (um em modo cliente e outro access-point) e estas conversando por sinal wireless:

Fonte:https://wiki.openwrt.org/doc/howto/clientmode

O computador "Bridged Host" está conectado por cabo no cliente wireless "W-LAN Client". Este está conectado como um cliente wireless no "W-LAN AP" que, por fim, conecta em um segundo computador "Target". Esta seria a tentativa de conversa entre "Bridged Host" e "Target":
  1. "Bridged Host" prepara um pacote para "Target"  colocando seu endereço MAC de origem e o MAC do computador "Target" como destino. O pacote é enviado à estação "W-LAN Client".
  2. A estação "W-LAN Client" recebe o pacote e o envia para "W-LAN AP". Porém, antes de enviar, ela troca o MAC de origem, antes com o MAC do "Bridged Host", pelo seu próprio MAC. Caso contrário, a estação "W-LAN AP" não aceitaria.
  3. A estação "W-LAN AP" recebe o pacote e o retransmite para o computador "Target".  Pelo endereço MAC de origem, "Target" acha que quem está falando com ele é "W-LAN Client" e não "Bridged Host".
  4. A resposta é enviada para "W-LAN AP"
  5. "W-LAN AP" retransmite o pacote sem modificação pois a limitação de usar seu próprio MAC é dos clientes e não da estação.
  6. "W-LAN Client" não sabe do que se trata tal pacote e ignora
  7. "Bridged Host" nunca recebe uma resposta
Existem algumas soluções para este problema. Vou mostrar as mais interessantes.

WDS - Wireless Distribution System

A solução mais limpa e elegante é utilizar a tecnologia WDS. Com ela, a comunicação entre os equipamentos wireless preservam o MAC de origem. A figura é igual a anterior, com a diferença que, na comunicação entre o "W-LAN Client" e "W-LAN AP", é utilizado o padrão WDS.


Fonte:https://wiki.openwrt.org/doc/howto/clientmode

A figura mostra que existem 4 endereços MAC em todos os pacotes na comunicação wireless WDS: origem e destino das placas sem fio, e origem e destino dos dados. Assim, a informação das entidades envolvidas não é perdida e a comunicação pode se realizar. Porém, WDS depende da implementação do driver e dos softwares envolvidos. Se ambos forem o mesmo equipamento e com OpenWRT, não é problema. Se for com fornecedores diferentes, pode não funcionar.

Ponte com NAT (possivelmente duplo NAT)

Uma segunda solução seria transformar o equipamento "W-LAN Client" em um roteador, traduzindo os endereços por NAT.

Fonte:https://wiki.openwrt.org/doc/howto/clientmode

A conexão "W-LAN Client" funcionaria como uma conexão WAN. Isto é possível com o OpenWRT. Porém, seria a mesma situação do caso onde a ligação entre o roteador da internet e o roteador wireless era feita pela porta WAN: você terá duas NAT, os computadores da rede azul não falarão diretamente com os computadores da rede vermelha, exceto se existir um encaminhamento de porta.

Roteamento entre Redes

Uma última sugestão apresentada aqui seria o uso de roteamento. Ao invés de esconder os computadores atrás de uma NAT, usar uma outra subrede e configurar rotas estáticas nos roteadores da rede azul e vermelha.

Fonte:https://wiki.openwrt.org/doc/howto/clientmode


Esta é uma solução mais complexa de ser implementada. Porém, preserva a conectividade. Ainda assim, os computadores não estarão na mesma rede e algumas facilidades como a descoberta automática de serviços na rede local não irá funcionar entre as redes.

Ainda existem outras soluções como a utilização de um aplicativo que rastreia as conversas que passam pelo "W-LAN Client" e fazem a tradução dos endereços e outras alternativas baseadas em protocolos proprietários. Porém, acho que as 3 propostas apresentadas aqui são as mais recomendadas na maioria das situações.

No próximo artigo vou mostrar como transformar um segundo roteador em uma ponte entre as redes cabeadas e, ao mesmo tempo, um repetidor de sinal wireless.

Até a próxima.

quarta-feira, 1 de maio de 2013

OpenWrt: 12.09 chegou! Como faço o Upgrade?

Depois de um ano e pouco desde o último lançamento de uma versão estável (10.03.1), foi lançado o OpenWRT 12.09. Já sou usuário desta versão faz um bom tempo, desde o seu tempo de beta e RC. Recomendo-o aos atuais usuários do OpenWRT.

Como sempre, temos diversas melhorias, novas plataformas e modelos suportados na versão estável, pacotes atualizados. O kernel passou para 3.3, a interface deu uma melhorada, mais funcionalidades no firewall, mais fácil usar uma unidade externa como raiz, e assim vai...

Com a evolução do código, também ocorre outro efeito: algumas plataformas antigas são descontinuadas. Isto ocorreu para as plataformas que usavam driver fechado do fabricante atrelados ainda ao kernel 2.4 e sistemas com pouca memória RAM (16 MB). Em geral, isto não será problema aos roteadores comprados nos últimos anos. Também talvez não faria sentido atualizar seu roteador antigo pois sem memória RAM ele não teria recurso para usar grande parte das melhorias. É a sina da obsolescência programada.

Agora para quem possui um roteador compatível e já usa OpenWRT, deve estar coçando para atualizar. E como faço isto?

A escolha da imagem segue o mesmo princípio que é usado para escolher a imagem da primeira instalação. Selecione a imagem pela arquitetura, modelo e versão do hardware. Como sempre, recomendo a versão squashfs, que possui modo de recuperação. A diferença entre a primeira instalação e esta é que não será usada a imagem "factory" e sim a "sysupgrade". E muito importante, leia a wiki do seu roteador! Pode existir algo muito importante ali como o problema com o TL-MR3420 na versão 10.03.1.

Bem, se eu simplesmente ir na interface, fornecer a nova imagem, ele vai funcionar? Provavelmente, mas talvez não é a maneira que dê menos trabalho. Vamos a algumas sugestões:

Em primeiro lugar, faça um backup geral do seu sistema. Sugiro três coisas diferentes: backup gerado pelo openwrt, lista de pacotes instalados e todos os arquivos do overlay.

A primeira e mais simples é o backup gerado pelo openwrt. Este é obtido na mesma página de atualização de firmware do seu roteador. Ele contém uma seleção prévia de várias configurações, inclusive tudo que está em /etc/config. Contudo, o backup não irá manter todos os arquivos modificados em /etc ou em outro lugar. Por exemplo, se você fez alguma modificação no /etc/dnsmasq.conf, ele não será preservado. Se criou um script em /bin ou para os botões em /etc/hotplug.d/button, ele não será preservado. Para incluir estes e outros casos, informe o caminho destes arquivos extras em /etc/sysupgrade.conf. Na interface WEB também tem a edição deste arquivo, no mesmo local da atualização da firmware, na parte de configuração. Gere um arquivo de backup novo e verifique se tudo que você quer está lá dentro. Somente estes arquivos serão preservados em um upgrade.
Dica: olhe todo o conteúdo em /overlay. Ele terá tudo o que foi modificado. Cuide principalmente dos arquivos em /etc.
Porém, o backup do openwrt não é feito para guardar os programas instalados. Ele se limita a scripts, dados e arquivos de configuração. Programas instalados por pacotes devem ser reinstalados manualmente. Por isto a próxima sugestão.

A segunda sugestão é gerar uma listagem de todos os pacotes instalados. Esta pode ser obtido pela interface web ou pelo comando "opkg list-installed". Ela será usada de referência para reinstalar todos os seus programas. Como os programas não serão preservados (somente suas configurações e se estiverem no backup) você terá que reinstalá-los. Em geral, é uma meia dúzia de programas.

A terceira sugestão é fazer um backup completo de todo o /overlay. Afinal de contas, falamos de poucos megabytes mas dentro deles podem estar algumas horas de trabalho. A cópia pode ser feita com um tar. É provavel que você não tenha espaço para criar este tar diretamente no roteador. Você terá que fazê-lo jogando em um disco externo conectado pela USB ou, a forma que eu geralmente uso, diretamente pela rede. Pela rede seria assim:
meucomputador$ ssh root@roteador tar -czv /overlay | cat > overlay.tar.gz
A vantagem deste backup é que, se esquecer de colocar algo em /etc/sysupgrade.conf, você poderá recuperá-lo do arquivo tar.gz. Também, se precisar retornar ao firmware antigo, você já teria uma partição overlay pronta. Bastaria instalar a firmware antiga e jogar o conteúdo da overlay por cima.

Agora, finalmente, você está pronto para enviar a nova firmware. Se você usa um espaço externo para expandir o disco, leia até o final deste post. Faça o upgrade pela interface web ou pelo terminal. Inclusive, você pode baixar o arquivo diretamente no roteador. Ex:
cd /tmp
wget http://downloads.openwrt.org/attitude_adjustment/12.09/..../openwrt...xxx...img
sysupgrade openwrt...xxx....img 
E esperar para ver. Se optar por preservar as configurações, tudo que seria guardado em um backup do sistema, inclusive o que está listado em /etc/sysupgrade.conf, será automaticamente levado ao novo sistema. Depois de o sistema iniciar na próxima versão e estiver funcionando, é hora de retornar os programas antigos. Gere uma nova listagem dos programas instalados e compare com a listagem anterior.
Dica: vimdiff instalados.antigo.txt instalados.novo.txt
A mudança nas versões dos pacotes é esperada e pode ser desconsiderada. Para facilitar, tente instalar primeiro os pacotes que dependem de outros, como os pacotes luci-app-*, antes de instalar os demais. É provável que, pela cadeia de dependências, grande parte será automaticamente instalada. Repita a geração dos programas instalados, a comparação e a instalação até estar satisfeito.

Por fim, faça um novo backup geral. É sempre bom preservar o seu trabalho. 

Hum, e eu que uso um disco externo para expandir o espaço interno? É um pouco mais complicado. Ao atualizar o sistema, os módulos e programas que fariam o uso do disco externo irão parar de funcionar. Você precisaria reinstalá-los. Esta é a sugestão de como proceder:

Em primeiro lugar, gere todos os backups sugeridos anteriormente. É importante preservar seu trabalho anterior. Ainda sem instalar, reinicie o sistema sem o disco externo. Com isto, ele vai usar somente a flash interna (com a configuração que você tinha antes de usar o disco externo). Faça todos os backups novamente, preservando os anteriores. Agora à instalação.

Ainda com o disco externo desconectado, instale a nova firmware. Refaça a configuração de uso do disco externo (que no mínimo será reinstalar os pacotes necessários). No disco externo, na partição usada como overlay, remova todo o conteúdo ou mova tudo para um subdiretório afim de que este não seja usado. Como sugestão, crie um "owrt-10.03.1" e mova tudo para lá. Em seguida, popule novamente este disco da mesma maneira sugerida no artigo de expansão do disco interno (usando o tar). Reinicie o sistema.

Neste ponto, você ainda terá as mesmas configurações que tinha quando usou o sistema sem o disco externo. Envie o primeiro backup da versão anterior feito ainda com o disco externo e siga os passos de reinstalação dos pacotes, assim como é feito para ambientes sem o disco expandido. Complete o trabalho com aquele backup final.

Espero que apreciem a nova versão. De agora em diante, vou apenas focar em configurações específicas do 12.09, que ainda podem funcionar no 10.03.1.

Até mais.

domingo, 28 de abril de 2013

OpenWRT: Passo a passo da solução de um bug no kernel

Este post será mais um relato do que tenho feito nos últimos dias em relação a um bug muito estranho mas também poderá passar o caminho das pedras se alguém quiser se aventurar mais abaixo neste mundo Linux.

Um problema que encontrei após a configuração do HD externo pela USB foi que algumas partições não apareciam. Meu disco era grande e tinha este formato:

  • sda: disco de 1.5TB
    • sda1: partição primária NTFS de 60GB
    • sda2: partição estendida com o restante do disco
      • sda5: partição lógica ext4 com 1TB
      • sda6: partição lógica swap de 4GB
      • sda7: partição lógica ext4 com 60GB

Se não conhece o básico da estrutura da tabela de partições da MBR, o que é uma partição primária, estendida e lógica, wikipedia é sua amiga.

Nada muito anormal e nunca tive problemas durante anos de uso do disco. Porém, ao conectar no OpenWRT, a partição sda6 e sda7 não apareciam. Cumé? Como em geral acontece, os logs dão uma dica. Esta é a mensagem do kernel:

[  173.870000] sd 1:0:0:0: [sdb] No Caching mode page present
[  173.870000] sd 1:0:0:0: [sdb] Assuming drive cache: write through

[  180.330000]  sdb: sdb1 sdb2 < sdb5 >

[  180.340000] sdb: partition table partially beyond EOD, enabling native capacity

[  180.350000] sd 1:0:0:0: [sdb] No Caching mode page present
[  180.360000] sd 1:0:0:0: [sdb] Assuming drive cache: write through
[  180.370000]  sdb: sdb1 sdb2 < sdb5 >
[  180.370000] sdb: partition table partially beyond EOD, truncated
[  180.380000] sd 1:0:0:0: [sdb] No Caching mode page present
[  180.390000] sd 1:0:0:0: [sdb] Assuming drive cache: write through
[  180.400000] sd 1:0:0:0: [sdb] Attached SCSI disk
Bem, o que era possível entender era que, por algum motivo, o Linux no OpenWRT estava achando que uma partição estava além do fim do disco (EOD). Ele tentava ativar uma "native capacity", que não resolvia, e depois truncava o disco. No final de contas, eu ficava com as partições até a sda5.

Agora vem a beleza do software livre: eu tenho o fonte :-) Fazendo uma busca textual simples pela mensagem, eu encontrei este trecho de código:
linux-3.3.8/block/partition-generic.c:

int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
{
(...)
    state = check_partition(disk, bdev))
(...)
    /*
     * If any partition code tried to read beyond EOD, try
     * unlocking native capacity even if partition table is
     * successfully read as we could be missing some partitions.
     */
    if (state->access_beyond_eod) {
        printk(KERN_WARNING
               "%s: partition table partially beyond EOD, ",
               disk->disk_name);
        if (disk_unlock_native_capacity(disk))
            goto rescan;
    }
(...)
}
Bem, alguma coisa no check_partition(disk, bdev) estava marcando o estado state->access_beyond_eod e gerando a mensagem. Buscando pelo texto access_beyond_eod, achei que ele somente era definido por este código:
linux-3.3.8/block/partitions/check.h:
static inline void *read_part_sector(struct parsed_partitions *state,
                     sector_t n, Sector *p)
{
    if (n >= get_capacity(state->bdev->bd_disk)) {
        state->access_beyond_eod = true;
        return NULL;
    }

    return read_dev_sector(state->bdev, n, p);
}
Pelo código, somente será marcado access_beyond_eod se o setor lido for maior que o tamanho do disco. Agora, quem está errado? O setor? O tamanho do disco? O tamanho do disco parecia estar correto pois ele era apresentado corretamente nas mensagens de kernel e no arquivo /proc/partitions. Deve ser o setor então. Mas ele está errado no disco ou tratado incorretamente?

Para sanar a primeira dúvida, com ajuda da santa wikipedia, fiz um script para interpretar as tabelas de partição na MBR e na partição estendida. Neste ponto, tentando isolar o problema, já tinha apagado as partições 6 e 7, criado uma nova 6 com 1MB, reduzido a partição 1 e criado uma partição 3 entre as partições primárias 1 e 2. Nada disto alterou o resultado. O script mostrou estas informações:
Checking /dev/sdb...

Checking /dev/sdb1...present
Primary Entry: 0001010007FEFFFF3F000000C1676007
Start(CHS): (010100)
End  (CHS): (FEFFFF)
Code      : 07
Start(LBA): 63 (3F000000)
Size(sect): 123758529 sectors, 63364 Mbytes (C1676007)
Checking /dev/sdb2...present
Primary Entry: 00FEFFFF0FFEFFFF1A5E8007E62913A7
Start(CHS): (FEFFFF)
End  (CHS): (FEFFFF)
Code      : 0F
Start(LBA): 125853210 (1A5E8007)
Size(sect): 2803050982 sectors, 1435162 Mbytes (E62913A7)
Extended partition detected! Reading logical partitions...
Checking /dev/sdb5...present

Primary Entry: 00FEFFFF83FEFFFF3F0000002C140080
Active? no
Start(CHS): (FEFFFF)
End  (CHS): (FEFFFF)
Code      : 83
Start(LBA): 63 (3F000000)
Size(sect): 2147488812 sectors, 1099514 Mbytes (2C140080)
Secondary Entry: 00FEFFFF05FEFFFF6B1400807B150000
Next ERB(LBA): 2147488875 (6B140080)
Size(sect): 5499 sectors, 2 Mbytes (7B150000)
Checking /dev/sdb6...present

Primary Entry: 00FEFFFF83FEFFFF7B0D000000080000
Active? no
Start(CHS): (FEFFFF)
End  (CHS): (FEFFFF)
Code      : 83
Start(LBA): 3451 (7B0D0000)
Size(sect): 2048 sectors, 1 Mbytes (00080000)
Secondary Entry: 00000000000000000000000000000000
This is the last logical partition!


Checking /dev/sdb3...present
Primary Entry: 00FEFFFF82FEFFFF0068600700F01F00
Start(CHS): (FEFFFF)
End  (CHS): (FEFFFF)
Code      : 82
Start(LBA): 123758592 (00686007)
Size(sect): 2093056 sectors, 1071 Mbytes (00F01F00)
Checking /dev/sdb4...not present
Em resumo, por encontrar todas as partições, e com o tamanho correto, o disco estava íntegro. Era problema na leitura ou interpretação dos dados pelo kernel. Seria bem mais fácil o disco estar errado... Neste ponto, eu abri um bug para o OpenWRT mas continuei a investigar por conta própria.

O passo seguinte é adicionar mais informações de debug de dentro do kernel. A maneira mais simples é adicionar alguns "printk" (printf do kernel) em pontos estratégicos. Mas primeiro, é necessário encontrar estes pontos.

Meu disco, por ser menor que 2TB, ainda usa tabela de partições MS-DOS. Como o problema ocorre a partir da segunda partição lógica, o código deve estar na parte que lê os dados da partição estendida. O código é este:
linux-3.3.8/block/partitions/msdos.c
static void parse_extended(struct parsed_partitions *state,
                sector_t first_sector, sector_t first_size)
    this_sector = first_sector;

    while (1) {
(...)
        data = read_part_sector(state, this_sector, &sect);
(...)

        p = (struct partition *) (data + 0x1be);
(...)

        for (i=0; i<4; i++, p++)
            if (nr_sects(p) && is_extended_partition(p))
                break;
(...)
         this_sector = first_sector + start_sect(p) * sector_size
                   }
(...)
}
Olha a função read_part_sector que pode marcar access_beyond_eod ali no começo! Para conseguir entender o código, é necessário conhecer a estrutura das partições estendidas. Em resumo, a partição estendida possui uma tabela de partição própria (ERB), onde a primeira entrada descreve a primeira partição lógica e a segunda entrada aponta para a próxima tabela de partição. Voltando as aulas de estrutura de dados, é uma lista ligada/encadeada. Eu coloquei printk ao longo de toda esta função para descobrir o que não estava legal. A parte estranha foi justamente esta última, que soma a posição relativa da próxima tabela de partição com o início da partição estendida:

[   98.230000] parse_extended: sector_size = 1

[   98.230000] parse_extended: start_sect(p) = 2147488875
[   98.230000] parse_extended: start_sect(p)*sector_size = 2147488875
[   98.240000] parse_extended: first_sector = 125853210
[   98.250000] parse_extended: first_sector + start_sect(p) * sector_size = this_sector = 18446744071687926405
O que?! 2147488875 + 125853210 = 18446744071687926405 ?? O que aconteceu com  2273342085? Agora que tinha algo mais palpável, mandei diretamente para a lista de desenvolvimento do OpenWRT. Com ajuda de outros desenvolvedores, descobrimos o problema.

Em primeiro lugar, um pouco de contextualização. Todas as variáveis na expressão problemática são do tipo sector_t, que para meu caso é representado por um u64 (valor inteiro sem sinal de 64 bits). Não deveria ter problemas para as grandezas envolvidas pois vai até 18446744073709551615. Além disto, os setores em uma partição MS-DOS são representados por inteiros sem sinal de 32-bit. Então o 64-bit estão sobrando. Por que o problema? Olhando o binário da resposta correta, 2273342085, e a errada, podemos ver uma coisa em comum:

2273342085(b10)           = 0000000000000000000000 1000101011000000111110(b2)
18446744071687926405(b10) = 1111111111111111111111 1000101011000000111110(b2)

Os bits mais significativos ficaram todos 1 quando o correto seria 0. Uma possível causa deste monte de '1' é de um resultado da conversão de um inteiro com sinal 32-bit para 64-bit. Voltando a aula de análise numérica (wikipedia para relembrar?), os números inteiros com sinal serão negativos quando o primeiro bit for 1. Porém, não é simplesmente colocar um bit na frente. O sistema mais usado atualmente é o complemento para dois. O número negativo é a negação dos bits do mesmo número positivo, mas deslocando em um para não ter zero positivo e negativo. O que importa para este problema é que, para converter um número para outro igual com mais bits, basta completar os bits mais significativos com o valor do primeiro bit. A tabela seguinte mostra exemplo desta conversão de 4-bit para 8-bit:

Decimal
4-bit
8-bit
+3
0011
00000011
+2
0010
00000010
+1
0001
00000001
0
0000
00000000
−1
1111
11111111
−2
1110
11111110
−3
1101
11111101


E o mesmo vale de 32-bit para 64-bit. Como o primeiro bit de 2273342085 é 1, se este fosse um número negativo, ao converter para 64-bit, todos seus primeiros bits serão 1. Mas eu não tinha falado que as variáveis daquela expressão problemática são sem sinal? O compilador se perdeu? Só uma forma de dizer isto: olhando o assembly gerado.

Recompilando o kernel com informações de debug ativadas, é possível extrair o assembly em conjunto com o código em C. A arquitetura do roteador é MIPS. Enfim as aulas de assembly da faculdade com o MIPS servem para alguma coisa! O resultado é este:

this_sector = first_sector + start_sect(p) * sector_size;
 344:   02e00013        mtlo    s7
 348:   00052a00        sll     a1,a1,0x8
 34c:   00031c00        sll     v1,v1,0x10
 350:   00a31825        or      v1,a1,v1
 354:   90850008        lbu     a1,8(a0)
 358:   9084000b        lbu     a0,11(a0)
 35c:   02c00011        mthi    s6
 360:   00651825        or      v1,v1,a1
 364:   00042600        sll     a0,a0,0x18
 368:   00641825        or      v1,v1,a0
 36c:   70720000        madd    v1,s2
 370:   00005812        mflo    t3
 374:   afab004c        sw      t3,76(sp)
 378:   00005010        mfhi    t2
 37c:   afaa0048        sw      t2,72(sp)
A parte que importa está em destaque. A instrução madd é a soma entre dois números inteiros com sinal. O correto deveria ser maddu. Então o compilador acha que isto é um inteiro com sinal? Beleza, compilador com bug, joga fora e pega outro.... não é tão simples assim. Não existe outro compilador que possa ser usado além do gcc para compilar o kernel (llvm está quase lá, mas não para MIPS). Será mesmo problema do compilador? Pode ser, mas pode também ter alguma ajuda do código. Voltando a linha problemática, fora as variáveis locais, existe uma chamada externa para start_sect. Este é o código:
linux-3.3.8/block/partitions/msdos.c:
static inline sector_t start_sect(struct partition *p)
{
    return (sector_t)get_unaligned_le32(&p->start_sect);
}
Ele pega a posição do setor inicial na lista de entradas da tabela de partição, que representa em um número 32-bit, com formato big endian, na mesma ordem usada em protocolos de rede. Este buffer de 4 bytes é passado para a função get_unaligned_le32, que converte para o formato de número 32-bit na ordem da máquina. A implementação desta função depende de cada arquitetura. Em processadores x86, existe uma instrução do processador para fazer isto. Para MIPS, é feiro por código:
linux-3.3.8/include/linux/unaligned/le_byteshift.h:
static inline u32 __get_unaligned_le32(const u8 *p)
{
    return p[0] |p[1] << 8 | p[2] << 16 | p[3] << 24;
}
O código é simples: ele pega cada um dos 4 bytes do buffer 32-bit, desloca cada um para a sua posição correta usando o operador left shift e junta tudo com o operador ou. E qual o problema disto? Existe um não aparente. A especificação C99 define que:


   The integer promotions are performed on each of the operands. The type of the result is
   that of the promoted left operand. If the value of the right operand is negative or is
   greater than or equal to the width of the promoted left operand, the behavior is undefined.

Então, se o lado esquerdo possuir menos ou o mesmo número de bits deslocados pelo lado direito do operador, o comportamento não é definido, que em miúdos quer dizer, se você fizer isto, não sei o que vai acontecer. Nisto, o compilador se perdeu e, por algum motivo, ignorou o fato das variáveis serem sem sinal. Pode ser que ele ainda estivesse considerando operações para variáveis sem sinal 8-bit, onde não importa muito se você está usando um operador 32-bit com ou sem sinal. A solução é ligeiramente simples: converter o tipo do lado esquerdo antes de aplicar o operador. Fica assim:
linux-3.3.8/include/linux/unaligned/le_byteshift.h:
static inline u32 __get_unaligned_le32(const u8 *p)
{
    return p[0] | (u32)p[1] << 8 | (u32)p[2] << 16 | (u32)p[3] << 24;
}

E o código assembly indica que o compilador, agora, assume que aquela soma é entre variáveis sem sinal:

 this_sector = first_sector + start_sect(p) * sector_size;
 344:   02e00013        mtlo    s7
 348:   00052a00        sll     a1,a1,0x8
 34c:   00031c00        sll     v1,v1,0x10
 350:   00a31825        or      v1,a1,v1
 354:   90850008        lbu     a1,8(a0)
 358:   9084000b        lbu     a0,11(a0)
 35c:   02c00011        mthi    s6
 360:   00651825        or      v1,v1,a1
 364:   00042600        sll     a0,a0,0x18
 368:   00641825        or      v1,v1,a0
 36c:   70720001        maddu   v1,s2
 370:   00005812        mflo    t3
 374:   afab004c        sw      t3,76(sp)
 378:   00005010        mfhi    t2
 37c:   afaa0048        sw      t2,72(sp)
Note que a instrução mudou de madd para maddu. Provavelmente foram alteradas outras instruções no código mas ao menos agora sei que o compilador está tratando como número sem sinal.

Adicionado o patch ao kernel, recompilando a imagem e instalando, volto a ter acesso a todas as partições do disco!

Acredito que este problema ainda era desconhecido por ser raro juntar uma arquitetura vulnerável ao problema com ao menos uma partição lógica iniciada além do setor 4294967296 (ou 1 TB) na partição. O mundo x86 não é afetado pois a função __get_unaligned_le32 é implementada diretamente por uma instrução do processador.


Agora falta apenas submeter oficialmente o patch para o Openwrt e para o kernel oficial. Enfim, a culpa era do kernel Linux e compilador, não diretamente do OpenWRT.

Até a próxima!