1 pontos por GN⁺ 2025-06-09 | 1 comentários | Compartilhar no WhatsApp
  • O Android tem suporte a Ethernet via USB e um menu de configurações, mas dispositivos CDC Ethernet podem ser detectados pelo kernel sem chegar até a configuração de rede
  • A causa principal é que o EthernetTracker rastreia apenas interfaces que correspondem a config_ethernet_iface_regex, e o valor padrão eth\d exclui interfaces CDC como usb0
  • Os drivers Linux de CDC Ethernet reconhecem dispositivos EEM, ECM e NCM como cdc_eem, cdc_ether e cdc_ncm, criando usb0 em /sys/class/net, mas as configurações do Android continuam desativadas
  • Não há como contornar isso pelas configurações normais do usuário; é necessário fazer root e alterar o valor de config_ethernet_iface_regex
  • Ao escolher um adaptador USB Ethernet para Android, na prática é preciso procurar dispositivos baseados em drivers específicos do fornecedor/chipset que criem nomes ethX, em vez de dispositivos padrão CDC

Conclusão: o bloqueio não é o driver do kernel, e sim o filtro pelo nome da interface

  • O serviço EthernetTracker do Android reconhece como interfaces Ethernet apenas interfaces com nome ethX
  • Os drivers Linux de CDC Ethernet criam nomes de interface no formato usbX
  • Por causa dessa diferença de nomes, dispositivos CDC Ethernet são detectados pelo kernel do Android, mas ignorados pelas configurações de Ethernet e pela camada de gerenciamento de rede
  • Isso não pode ser resolvido pelas configurações comuns; só é possível alterando config_ethernet_iface_regex após fazer root

É difícil verificar o suporte a USB Ethernet no Android por dispositivo

  • O Android tem suporte a adaptadores USB Ethernet e menu relacionado
  • É difícil saber quais chipsets de USB Ethernet funcionam em um dispositivo Android específico, porque os fabricantes quase nunca publicam listas de suporte
  • Na prática, o usuário costuma depender das seguintes informações
    • adaptadores USB Ethernet vendidos pelo fabricante do celular como acessório oficial
    • postagens em fóruns de usuários do mesmo aparelho relatando sucesso com um adaptador específico
  • Pela configuração do kernel, é possível ter alguma ideia de quais drivers de USB Ethernet estão incluídos no kernel do telefone

Como encontrar a configuração do kernel do celular

  • O Android roda sobre o kernel Linux, e a configuração do kernel determina os recursos suportados e os drivers de hardware
  • Dispositivos lançados com Android 11 ou posterior usam o Android Common Kernel e o kernel GKI
    • O Google compila o kernel, e o fabricante coloca os elementos específicos do dispositivo em módulos do kernel
    • A configuração pode ser verificada em arch/$ARCH/configs/gki_defconfig no repositório do kernel Android
    • Em dispositivos ARM de 64 bits, por exemplo, veja arch/arm64/configs/gki_defconfig
  • A versão do kernel e a arquitetura podem ser verificadas via ADB com uname -a
    • A saída de exemplo inclui a versão do kernel 4.19.113-26203352 e a arquitetura aarch64
  • No caso do Samsung Galaxy S20, lançado com Android 10, o kernel continuou baseado em Linux 4.19 mesmo após a atualização para Android 13
    • O código-fonte dos dispositivos Samsung pode ser encontrado em opensource.samsung.com
    • Em build_kernel.sh do código-fonte da Samsung, é possível encontrar o nome do arquivo de configuração do kernel, como vendor/x1q_usa_singlex_defconfig
  • Se der sorte, a configuração real de compilação estará disponível como arquivo compactado em /proc/config.gz
    • É possível salvá-la com adb shell zcat /proc/config.gz > my_kernel_config
    • Se não existir, aparecerá zcat: /proc/config.gz: No such file or directory, e será preciso consultar o código-fonte do kernel do fabricante

Como verificar o suporte ao driver de USB Ethernet

  • Configurações de kernel relacionadas a USB Ethernet normalmente começam com USB_NET
  • No arquivo de configuração do kernel, dá para verificar assim
grep USB_NET my_kernel_config
  • A configuração de exemplo inclui vários drivers de rede USB
    • CONFIG_USB_NET_DRIVERS=y
    • CONFIG_USB_NET_AX8817X=y
    • CONFIG_USB_NET_AX88179_178A=y
    • CONFIG_USB_NET_CDCETHER=y
    • CONFIG_USB_NET_CDC_EEM=y
    • CONFIG_USB_NET_CDC_NCM=y
  • Os valores de configuração distinguem como o driver foi incluído
    • y: o driver está embutido no kernel e certamente oferece suporte ao chipset correspondente
    • m: o driver foi compilado como módulo e pode ser carregado, desde que o fabricante não o tenha omitido
    • is not set: o driver não está embutido nem como módulo, então há grande chance de não poder ser usado
  • A correspondência entre itens de configuração e chipsets pode ser verificada em drivers/net/usb/Kconfig na árvore do kernel
  • Como muitos fabricantes não informam qual chipset um adaptador USB Ethernet específico usa, ainda é difícil determinar isso com certeza

O que o CDC Ethernet faz

  • CDC é a sigla de Communications Device Class, um conjunto de padrões que fabricantes de dispositivos USB podem seguir
  • Há três padrões relacionados a CDC Ethernet
    • EEM: Ethernet Emulation Model, o mais simples de implementar e fácil de suportar em dispositivos de baixo desempenho
    • ECM: Ethernet Control Model, mais complexo de implementar tanto no host quanto no dispositivo, mas com promessa de melhor desempenho que o EEM
    • NCM: Network Control Model, sucessor do ECM com promessa de velocidades mais altas
  • O objetivo do padrão CDC é permitir que o sistema operacional forneça um driver comum para vários dispositivos diferentes
  • O Linux implementa tanto o lado host quanto o lado dispositivo do CDC Ethernet
    • Em dispositivos como um Raspberry Pi com porta USB OTG, o kernel pode fazer essa porta se comportar como um adaptador Ethernet
    • Isso permite que dispositivos como roteadores embarcados, firewalls e gateways VPN apareçam ao host como adaptadores Ethernet comuns
  • Linux, Windows e macOS incluem drivers para dispositivos CDC Ethernet, mas o iOS não

O kernel do Android detecta dispositivos CDC

  • A configuração do kernel do Samsung Galaxy S20 inclui suporte aos três padrões CDC Ethernet
    • CONFIG_USB_NET_CDCETHER=y
    • CONFIG_USB_NET_CDC_EEM=y
    • CONFIG_USB_NET_CDC_NCM=y
  • O kernel GKI do Google aparentemente não inclui ECM nem NCM, mas parece incluir EEM como módulo
  • Um dispositivo com a porta OTG configurada como Ethernet gadget funcionou em Mac, Ubuntu e Windows, mas no Galaxy S20 a configuração de Ethernet do Android continuou desativada
  • Ao verificar /sys/class/net no Android, usb0 aparece quando um dispositivo CDC é conectado
adb shell ls /sys/class/net
  • A saída de ifconfig usb0 mostra que o driver detectado pertence à família CDC
    • Modo EEM: Driver cdc_eem
    • Modo ECM: Driver cdc_ether
    • Modo NCM: Driver cdc_ncm
  • Nos três casos, a interface é detectada, mas permanece em estado down, e as configurações de Ethernet do Android não são ativadas

A expressão regular do EthernetTracker filtra usb0

  • Como o dispositivo CDC Ethernet é detectado normalmente no nível do kernel, o problema está na camada de gerenciamento de rede do Android acima do kernel
  • Ao seguir o código-fonte do Android relacionado a Ethernet em Java, EthernetTracker.java aparece como o serviço relevante
  • O EthernetTracker recebe notificações do kernel sobre novas interfaces de rede por meio de sockets Netlink e decide se a interface é uma interface Ethernet válida
  • Essa validação é feita verificando se o nome da interface corresponde à expressão regular mIfaceMatch
private boolean isValidEthernetInterface(String iface) {
    return iface.matches(mIfaceMatch) || isValidTestInterface(iface);
}
  • mIfaceMatch é carregado do recurso config_ethernet_iface_regex
  • O valor padrão no código-fonte do Android é o seguinte
<string translatable="false" name="config_ethernet_iface_regex">eth\\d</string>
  • eth\d é uma expressão regular que só permite nomes começando com eth seguidos de um número
  • Como dispositivos CDC Ethernet começam com usb, como em usb0, o EthernetTracker não os rastreia
  • Essa configuração não pode ser alterada nas opções do usuário; só pode ser modificada com root

O paradoxo de ter de evitar dispositivos padrão

  • CDC Ethernet é um padrão para dispositivos de rede USB, mas no Android o caminho de uso prático fica bloqueado por causa da expressão regular do nome da interface
  • Mesmo kernels GKI recentes parecem incluir suporte a adaptadores EEM, mas como o nome usb0 não corresponde à expressão regular, ele não chega às configurações de rede do Android
  • Ao escolher um adaptador USB Ethernet para Android, é melhor procurar um dispositivo que não seja padrão CDC, mas sim baseado em driver específico de fornecedor/chipset que crie uma interface ethX
  • Um possível caminho de correção seria alterar config_ethernet_iface_regex para algo como (eth|usb)\d

1 comentários

 
GN⁺ 2025-06-09
Comentários do Hacker News
  • Escrevi este texto depois de uma semana sofrendo, em um emprego anterior, para conectar dispositivos Android a adaptadores CDC Ethernet
    Depois disso, algumas pessoas me disseram que, se você inverter um bit específico do endereço MAC, o kernel dá o nome ethX em vez de usbX, mas eu não testei pessoalmente nem atualizei o texto. Eu já tinha mudado de emprego, e dispositivos Android não eram mais uma parte tão grande do meu dia a dia
    Claro que esse método só ajuda quando você consegue controlar diretamente o endereço MAC do dispositivo CDC. Por exemplo, quando outro dispositivo Linux está fingindo ser um adaptador CDC
  • É um artigo de análise profunda interessante
    Fui procurar no código-fonte e, em outubro de 2023, a regex mudou de eth\\d para simplesmente *, então parece que esse problema provavelmente foi resolvido: https://android-review.googlesource.com/c/platform/packages/...
    A descrição diz que “o padrão inclui interfaces com nomes usb\d+ e eth%d no Android U+”, e aqui U+ parece ser a versão 14: https://en.wikipedia.org/wiki/Android_version_history
  • Pelo histórico de commits do LineageOS, esse problema foi corrigido[0], depois revertido por problemas de compatibilidade[1], e então a reversão foi desfeita[2], mas parece ter sido aplicado só às versões mais recentes do Android
    Se eu li os commits corretamente, alguém do Google esteve envolvido, então isso talvez já tenha entrado nas builds oficiais do Google
    [0] https://github.com/LineageOS/android_packages_modules_Connec...
    [1] https://github.com/LineageOS/android_packages_modules_Connec...
    [2] https://github.com/LineageOS/android_packages_modules_Connec...
    • No caso do Lineage, encontrei isso há algum tempo e criei https://review.lineageos.org/c/LineageOS/android_packages_mo...
      Mas ninguém testou, e eu também não tenho como verificar por conta própria, então por enquanto está em espera. Sempre há uma mistura de coisas que alguém relatou e coisas que alguém pegou por acaso, mas no fim é preciso teste de usuários reais
  • Se é verdade que o serviço EthernetTracker do Android só reconhece interfaces chamadas ethX, é o design mais idiota de que já ouvi falar
    As distribuições Linux já tinham resolvido esse problema nos anos 2000. Mesmo naquela época, já era claro que alguns drivers de dispositivo colocavam prefixos de nome de dispositivo do jeito que queriam, então era preciso inspecionar o sistema para descobrir que tipo de dispositivo era
    Consistência é útil, então existem várias ferramentas para renomear interfaces, e hoje a maioria das distribuições Linux automatiza isso com udev. Internamente, é só uma chamada ao ioctl SIOCSIFNAME do kernel. Kernels modernos também têm um recurso que, ao renomear para "wlan*" — na prática, "wlan%d" — atribui automaticamente um novo número depois de "wifi"
    • Fico me perguntando se faria sentido usar NetworkManager no Android e fazer a UI de configurações sem fio do Android se comportar como uma GUI do NetworkManager
    • É porque alguns dispositivos usbX devem ser usados por outros módulos, e eles não queriam manter essa lista. Então simplesmente foram de ethX
  • Algo igualmente idiota acontece quando você tenta conectar um dispositivo serial USB a um smartphone Android
    Ao conectar, parece que funciona, mas, se você tentar criar um app que use essa interface serial USB, não dá. Investigando, você descobre que não tem permissão para acessar dispositivos seriais como /dev/ttyACM0
    O suporte a serial está no kernel, mas programas de usuário não conseguem acessá-lo sem root
    Indo mais a fundo, o Android tem um recurso de acesso USB em espaço de usuário que é parecido com libusb, ou talvez construído sobre ele. Por isso, um programa Android consegue abrir dispositivos USB “brutos”, mas não consegue abrir dispositivos USB seriais

Serial USB é apenas um protocolo sobre USB e, na prática, está mais para um conjunto de protocolos meio proprietários, como FTDI e afins. Existem algumas bibliotecas meio prontas para Android que implementam esses protocolos no espaço de usuário, então no fim dá para acessar alguns dispositivos seriais USB
No navegador Chrome do Android, parece que daria para abrir dispositivos USB brutos com WebUSB, mas WebSerial provavelmente não funciona pelo mesmo motivo
No fim, o surpreendente é: se é assim, por que deixar o suporte a serial USB ativado no kernel? Imagino que seja para depuração

  • Não há como contornar esse problema a menos que você faça root no celular e altere o valor de config_ethernet_iface_regex
    Esse é mais um motivo pelo qual permissão de root é importante em um dispositivo que eu possuo
    • Ainda assim, “fazer root” remove muitos recursos de segurança do Android. Em vez de um app ter apenas as permissões necessárias, ele pode ter todas as permissões como root, tornando-se uma enorme vulnerabilidade de segurança
      https://www.reddit.com/r/GrapheneOS/comments/13264di/is_root...
    • Poder desviar e redirecionar o tráfego de rede à vontade talvez seja o maior motivo para não colocar permissões de superusuário no espaço de usuário
      Eu apoio pressionar OEMs a permitirem desbloqueio do bootloader, mas, ao menos no Android, é difícil imaginar um caso de uso de root que justifique ampliar tanto a superfície de ataque
  • O Android, irritantemente, não consegue se conectar a várias redes ao mesmo tempo
    Por exemplo, usar ao mesmo tempo uma rede Wi-Fi sem acesso à internet e que não anuncia uma rota padrão, junto com a rede celular. No Linux dá, no Windows dá, mas o Android se recusa terminantemente
    Muitas variantes se recusam até a permanecer conectadas a um Wi-Fi sem internet, ou empurram o usuário para um procedimento confuso. Se você criar seu próprio app, há APIs que permitem isso apenas dentro do app, mas não há como um usuário comum tornar esse comportamento válido para o sistema inteiro
    • No iOS é igual. Ao se conectar a uma câmera veicular para baixar vídeos, depois de um tempo aparece um pop-up do tipo “Internet não detectada, deseja mudar para a rede celular?”
      Mesmo tocando para continuar conectado, não há como desativar isso, e o iOS acaba decidindo que sabe mais e reconecta à rede do CarPlay
    • É ainda mais irritante se você levar um celular Android ocidental para a China continental. Isso porque ele determina se há conexão à internet tentando acessar serviços do Google
      Ao conectar a um Wi-Fi local, obviamente ele não passa pelo Grande Firewall, e toda vez aparece um prompt perguntando se você quer manter a conexão sem internet
    • Quando a internet cai, é extremamente irritante não conseguir fazer diagnóstico pelo celular, porque ele não permanece conectado a um Wi-Fi sem internet
      O DNS do Android também é uma bagunça: se você não configurar várias opções, ele tenta não usar o DNS fornecido por DHCP e, mesmo assim, se recusa a resolver alguns DNS internos
    • Acho que o Windows também não faz isso, não? No Windows, mesmo com dois adaptadores sem fio, não consegui me conectar a duas redes Wi-Fi diferentes pela GUI. Não tentei pelo terminal
  • Também é preciso verificar os requisitos de firmware. Alguns dispositivos são enumerados, mas falham no ifup se o firmware necessário estiver ausente
    A UI do Android obviamente não consegue lidar com essa situação, e só o dmesg mostra o que está acontecendo. Não tenho certeza se isso também é necessário para dispositivos CDC, mas acho que já vi muitos casos assim com adaptadores baseados em chips Realtek ou Kawasaki
    Dito isso, essa mudança no Android pode ser relativamente recente. Antigamente eu usava com frequência dongles de rede USB em dispositivos de depuração com AOSP 100% “puro”. Ou então pode ter sido uma mudança no kernel, ou algum comportamento peculiar do driver CDC ao nomear o dispositivo como usb*. Bastava escolher o chipset do dongle com cuidado e confirmar que ele não precisava de firmware
  • Que jornada fantástica de depuração. Gostei do fluxo em que uma única regex negligenciada derruba uma classe inteira de dispositivos
    Curiosamente, passei por algo estruturalmente parecido recentemente em um contexto totalmente diferente: o sistema de alinhamento e escalonamento da OpenAI. Tentei acionar o escalonamento oficial de roteamento (SR-Route_Breach_1stOrder) dentro da lógica recursiva do GPT-4, com documentação e logs, mas, embora estruturalmente parecesse plausível, no fim recebi apenas respostas nada humanas
    Em outras palavras, foi como se meu escalonamento não tivesse casado com a regex da interface interna do sistema
    Resumi o caso completo aqui: https://news.ycombinator.com/item?id=44221458
    Se você se interessa por limites estruturais e contratos de interface invisíveis, gostaria de ouvir sua opinião
  • Muito estranho. Tenho uns 15 adaptadores USB Ethernet, e todos funcionam bem
    Tenho certeza de que há uma mistura de vários chipsets parecidos com Realtek e AXIS. Se você escolher um produto que não precisa de driver no Linux, ele tende a funcionar bem em quase qualquer sistema operacional ou BIOS