GPON

GenieACS e TR-069: provisionamento automático de ONU Huawei

Engrenagens sobre uma ONU Huawei ligada ao servidor ACS, representando o provisionamento por TR-069

Trocar o nome e a senha do Wi-Fi de um cliente parece pouco até virar fila: o atendimento abre o chamado, alguém entra na ONU pela gerência, digita os dados e torce para não errar. Com o GenieACS, um servidor de auto-configuração (ACS) livre que fala TR-069, a ONU se apresenta sozinha ao servidor e recebe a configuração sem ninguém abrir a interface dela.

Eu uso GenieACS em produção com ONTs Huawei da linha EG8145 ligadas em OLTs MA5800, e este guia junta o que precisa estar certo nos dois lados: o TR-069 no line profile da OLT, a WAN de gestão da ONU, a instalação do servidor, os presets e provisions que aplicam Wi-Fi, a API e a proteção do ACS, que manda em todas as ONUs da base.

Resposta rápida: o que precisa existir para o GenieACS configurar a ONU

  1. Uma WAN de gestão na ONU, entregue pela OLT por OMCI, com IP que alcance o ACS (no exemplo, DHCP na VLAN 40).
  2. O tr069-management ligado no line profile e apontando para essa WAN com ip-index.
  3. A ONU sabendo o endereço do ACS e o ACS escutando na porta 7547 (CWMP).
  4. No GenieACS, um preset que diz quais ONUs recebem a configuração e um provision que diz o que configurar.
  5. A API (NBI) fechada para a internet, já que ela não pede login.

Como o TR-069 funciona

O TR-069, também chamado de CWMP, é o protocolo do Broadband Forum para gerenciar equipamentos de cliente. Ele roda sobre HTTP ou HTTPS, com mensagens SOAP. O ponto que mais confunde quem vem do SNMP: quem abre a sessão é sempre a ONU. Ela se conecta ao ACS, manda uma mensagem Inform dizendo quem é e por que está ligando, e a partir daí o ACS faz os pedidos dentro dessa mesma sessão.

Os pedidos são chamadas como GetParameterValues, SetParameterValues, AddObject, Reboot e Download (firmware), sobre uma árvore de parâmetros. As ONTs Huawei que uso falam o modelo TR-098, com raiz InternetGatewayDevice.; equipamentos mais novos podem usar o TR-181, com raiz Device..

Inform periódico e connection request

Se a ONU só ligasse no boot, o ACS ficaria dias sem notícia. Por isso existe o inform periódico: a ONU liga de novo a cada PeriodicInformInterval segundos. Para não esperar esse ciclo, o ACS pode mandar um connection request, um HTTP GET com usuário e senha para um endereço da própria ONU. A ONU confere as credenciais e abre uma sessão nova, com o evento 6.

Daí sai uma exigência de rede que derruba muita implantação: o connection request só funciona se o ACS alcançar o IP da WAN de gestão da ONU. Se esse IP está atrás de NAT ou numa VLAN sem rota de volta, as tarefas ficam na fila até o próximo inform.

Os eventos que importam

EventoQuando apareceO que o ACS costuma fazer
0 BOOTSTRAPPrimeiro contato com este ACS, ou depois de reset de fábricaLimpar o cache do modelo de dados e aplicar a configuração inicial
2 PERIODICChegou a hora do inform periódicoAplicar o que estiver pendente
6 CONNECTION REQUESTA sessão foi pedida pelo ACSExecutar a tarefa que motivou o pedido
7 TRANSFER COMPLETETerminou um download (firmware ou arquivo)Reiniciar ou validar a versão
Fluxo TR-069 entre OLT Huawei, ONU e GenieACS: WAN de gestão, Inform, SetParameterValues e connection request
A OLT entrega a WAN de gestão por OMCI; daí em diante a ONU conversa direto com o ACS.

O lado da OLT Huawei: WAN de gestão e tr069-management

A OLT não fala TR-069. O papel dela é preparar a ONU por OMCI: criar a WAN por onde o TR-069 vai passar e dizer, no line profile, que essa WAN é a de gerência. Se a OLT ainda está crua, comece pelas configurações iniciais da OLT Huawei.

Conferir o line profile

display ont-lineprofile gpon all
display ont-lineprofile gpon profile-id 10

O primeiro comando lista os perfis com ID e nome. O segundo mostra o perfil em uso pelas ONUs; procure as linhas de TR069:

  TR069 management        : Enable
  TR069 IP index          : 1

Uma armadilha que aprendi ao padronizar várias OLTs: o mesmo perfil, com o mesmo nome, tem profile-id diferente em cada OLT. Em automação, resolva o ID pelo nome em cada OLT e nunca copie o número de uma para a outra.

Ligar o TR-069 no perfil

ont-lineprofile gpon profile-id 10
 tr069-management enable
 tr069-management ip-index 1
 commit
 quit
display ont-lineprofile gpon profile-id 10
save

O tr069-management enable liga a gerência TR-069 para as ONUs do perfil. O ip-index 1 diz que o TR-069 sai pela WAN de índice 1 da ONU, a mesma que vamos criar com ont ipconfig. O commit não é opcional: sem ele o comando é aceito, o prompt muda, e nada é aplicado. Por isso o bloco relê o perfil depois do commit, e só então grava com save, para a mudança sobreviver a um reboot da OLT.

Criar a WAN de gestão da ONU

interface gpon 0/1
ont ipconfig 0 1 ip-index 1 dhcp vlan 40 priority 2
quit
service-port vlan 40 gpon 0/1/0 ont 1 gemport 2 multi-service user-vlan 40 tag-transform translate inbound traffic-table name GESTAO outbound traffic-table name GESTAO
display ont wan-info 0/1 0 1

O ont ipconfig cria na ONU 1 da porta 0 a WAN de índice 1, em DHCP, na VLAN 40, com prioridade 2. O service-port leva essa VLAN da GEM 2 da ONU até a OLT, com uma tabela de tráfego de gestão. O display ont wan-info confirma se a WAN pegou IP. Sem IP nessa WAN, o GenieACS nunca vai ver a ONU. O passo a passo completo do cadastro, com perfis e tabelas de tráfego, está no guia de como provisionar ONU na OLT Huawei.

A ONU também precisa saber o endereço do ACS. Esse endereço fica na configuração da própria ONU e pode chegar de mais de um jeito: gravado na configuração padrão que o provedor aplica, ou entregue pelo DHCP da VLAN de gestão, que o TR-069 prevê pela opção 43. Use o que o seu modelo de ONU e o seu servidor DHCP suportam, e teste numa ONU antes de mexer no perfil de produção.

Instalar o GenieACS

O guia oficial instala pelo npm e roda cada serviço como unidade do systemd, que é o que eu uso em produção. Imagens Docker existem, mas são mantidas pela comunidade, não pelo projeto; se preferir esse caminho, confira de quem é a imagem e qual versão ela empacota. Os pré-requisitos são Node.js e MongoDB nas versões indicadas na documentação oficial. O pacote 1.2.16 declara Node.js 12.13 ou mais novo; na prática, use uma versão LTS atual do Node.js e confira na documentação qual MongoDB ela suporta.

ServiçoPorta padrãoFunção
genieacs-cwmp7547Recebe as ONUs (TR-069). Precisa ficar acessível pela rede de gestão.
genieacs-nbi7557API REST para o seu sistema. Sem autenticação.
genieacs-fs7567Entrega arquivos (firmware) para as ONUs.
genieacs-ui3000Interface web, com login.
MongoDB27017Banco de dados do ACS.

Pacote, usuário e diretórios

sudo npm install -g genieacs@1.2.16
sudo useradd --system --no-create-home --user-group genieacs
sudo mkdir -p /opt/genieacs/ext /var/log/genieacs
sudo chown genieacs:genieacs /opt/genieacs/ext /var/log/genieacs

A versão 1.2.16 é a estável na data deste texto; confira a mais recente antes de instalar. O usuário de sistema roda os serviços sem privilégio de root, e o diretório ext guarda as extensões, que veremos adiante.

Arquivo de ambiente

Salve como /opt/genieacs/genieacs.env, no diretório criado acima. O guia oficial também define logs de acesso para o FS e a UI e um arquivo de debug; as duas últimas linhas não estão lá e prendem a API e a interface web ao localhost, como explico na seção de proteção.

GENIEACS_CWMP_ACCESS_LOG_FILE=/var/log/genieacs/cwmp-access.log
GENIEACS_NBI_ACCESS_LOG_FILE=/var/log/genieacs/nbi-access.log
GENIEACS_EXT_DIR=/opt/genieacs/ext
NODE_OPTIONS=--enable-source-maps
GENIEACS_NBI_INTERFACE=127.0.0.1
GENIEACS_UI_INTERFACE=127.0.0.1

Depois gere o segredo do JWT da interface web e feche as permissões do arquivo:

ENV=/opt/genieacs/genieacs.env
node -e "console.log('GENIEACS_UI_JWT_SECRET=' +
  require('crypto').randomBytes(128).toString('hex'))" \
  | sudo tee -a $ENV >/dev/null
sudo chown genieacs:genieacs $ENV && sudo chmod 600 $ENV

Unidades do systemd

Crie uma unidade por serviço (cwmp, nbi, fs e ui) como no guia oficial: usuário de sistema, EnvironmentFile apontando para o arquivo de ambiente e o executável do serviço em ExecStart. Depois habilite e suba as quatro:

for s in cwmp nbi fs ui; do
  sudo systemctl enable --now genieacs-$s
done

Os logs de processo vão para o journald (journalctl -u com o nome da unidade). A interface web sobe na porta 3000.

Presets e provisions: a lógica do provisionamento no GenieACS

O provision é um script JavaScript que roda no servidor para cada ONU e declara o estado desejado. O preset decide quando e em quem esse script roda: tem uma precondição (por exemplo, o modelo da ONU), pode reagir a eventos e aponta para um ou mais provisions.

O coração do script é a função declare(caminho, timestamps, valores). O timestamp diz quão recente o valor precisa ser: com {value: 1} o servidor confia no que já tem em cache; com {value: Date.now()} ele busca de novo na ONU nesta sessão. Se o valor desejado é diferente do atual, ele manda o SetParameterValues. O script roda quantas vezes for preciso até não haver mais mudança, então ele precisa ser idempotente.

bootstrap: limpar o cache depois de reset

const now = Date.now();
clear("Device", now);
clear("InternetGatewayDevice", now);

Vai num preset ligado ao evento 0 BOOTSTRAP: depois de um reset de fábrica, o clear obriga o ACS a reler a árvore da ONU em vez de confiar no cache.

inform: credenciais e intervalo

const MS = "InternetGatewayDevice.ManagementServer";
const user = declare("DeviceID.ID", {value: 1}).value[0];
const reg = declare("Events.Registered", {value: 1}).value[0];
const pass = Math.trunc(Math.random() * Number.MAX_SAFE_INTEGER)
  .toString(36);
const daily = Date.now(86400000);
const novo = reg > Date.now() - 86400000;
const intervalo = novo ? 3600
  : 86400 + Math.trunc(Math.random() * 7201);

declare(MS + ".ConnectionRequestUsername", {value: daily}, {value: user});
declare(MS + ".ConnectionRequestPassword", {value: daily}, {value: pass});
declare(MS + ".PeriodicInformEnable", {value: daily}, {value: true});
declare(MS + ".PeriodicInformInterval", {value: daily},
  {value: intervalo});

É uma versão do provision inform que vem com o GenieACS. A senha do connection request sai de Math.random(), que ali é semeado pelo ID do dispositivo, então é estável por ONU. O Date.now(86400000) arredonda o tempo para o dia: esses parâmetros só são conferidos uma vez por dia.

A mudança é o intervalo. O de fábrica é 300 segundos, tráfego demais com mil ONUs. Em produção eu uso 3600 segundos no primeiro dia da ONU e, depois, um valor sorteado entre 24 e 26 horas. Como o sorteio usa a mesma semente do ID, cada ONU fica sempre com o mesmo intervalo, e a base não informa toda no mesmo minuto depois de uma queda de energia.

Wi-Fi automático: virtual parameter, provision e extensão

O caso que mais economiza trabalho é o Wi-Fi: o ACS lê o login PPPoE da ONU, pergunta ao seu sistema o SSID e a senha daquele cliente e aplica nas duas bandas. O PPPoE eu deixo com a OLT, que já o grava por OMCI; dois donos para o mesmo parâmetro é receita para configuração que muda sozinha.

Virtual parameter com o login PPPoE

const ppp = "InternetGatewayDevice.WANDevice.1.WANConnectionDevice.*" +
  ".WANPPPConnection.*.Username";
const u = declare(ppp, {value: Date.now()});
let v = "";
for (const p of u) { if (p.value[0]) { v = p.value[0]; break; } }
return {writable: false, value: [v, "xsd:string"]};

Um virtual parameter aparece na árvore como VirtualParameters.Username. O script procura o usuário em qualquer conexão PPP da ONU e devolve o primeiro preenchido. Assim os provisions não precisam saber em qual instância o PPPoE foi criado, o que varia entre modelos.

Provision de Wi-Fi

const now = Date.now();
const W = "InternetGatewayDevice.LANDevice.1.WLANConfiguration";
const user = declare("VirtualParameters.Username", {value: now})
  .value[0];
const cfg = ext("erp", "wifi", user);

if (cfg && cfg.ssid) {
  for (const i of [1, 5]) {
    declare(W + "." + i + ".SSID", {value: 1}, {value: cfg.ssid});
    declare(W + "." + i + ".PreSharedKey.1.PreSharedKey",
      {value: 1}, {value: cfg.psk});
  }
} else {
  log("sem dados de Wi-Fi para " + user);
}

Nos modelos que uso, a instância 1 de WLANConfiguration é a rede de 2,4 GHz e a 5 é a de 5 GHz; confira no seu modelo antes. Repare no {value: 1} na senha: a ONU devolve a senha em branco quando alguém lê, então pedir leitura nova a cada sessão faria o servidor regravar a senha em todo inform. Se a API não tem dados do cliente, o script só registra no log e não escreve nada. Melhor uma ONU sem mudança do que uma ONU com SSID vazio.

A extensão que consulta o seu sistema

Provisions rodam numa sandbox sem acesso à rede. Para falar com o seu ERP ou banco, existem as extensões: módulos Node.js no diretório ext, chamados com ext(). Este fica em ext/erp.js:

"use strict";
const http = require("http");

function wifi(args, callback) {
  const url = "http://192.0.2.20/api/wifi/" +
    encodeURIComponent(args[0]);
  http.get(url, (res) => {
    let raw = "";
    res.on("data", (c) => (raw += c));
    res.on("end", () => {
      if (res.statusCode !== 200) return callback(null, null);
      try { callback(null, JSON.parse(raw)); }
      catch (e) { callback(null, null); }
    });
  }).on("error", (err) => callback(err));
}

exports.wifi = wifi;

Ligar tudo com um preset

Os provisions e presets podem ser criados pela interface web (Admin) ou pela API. Pela API, salve o provision em wifi.js e o preset em preset-wifi.json:

{
  "weight": 0,
  "channel": "wifi",
  "precondition": "DeviceID.ProductClass = \"EG8145V5\"",
  "events": {},
  "configurations": [{"type": "provision", "name": "wifi", "args": null}]
}
curl -i 'http://127.0.0.1:7557/provisions/wifi' -X PUT --data-binary @wifi.js
curl -i 'http://127.0.0.1:7557/presets/wifi' -X PUT --data-binary @preset-wifi.json

A precondição limita o preset ao modelo EG8145V5; sem ela, toda ONU de qualquer fabricante receberia o script. Comece com um grupo pequeno de clientes e confira o log antes de ampliar.

Descobrir parâmetros e mandar tarefas pela API (NBI)

O NBI é a API REST do GenieACS. É por ela que o seu sistema de atendimento pede uma troca de senha do Wi-Fi ou reinicia a ONU. Os exemplos rodam no próprio servidor, onde a API está presa:

ACS=http://127.0.0.1:7557
ONU=00259E-EG8145V5-48575443A1B2C3D4

Achar a ONU pelo serial

curl -s "$ACS/devices/?query=%7B%22_deviceId._SerialNumber%22%3A%2248575443A1B2C3D4%22%7D&projection=_id"

O _id do dispositivo é formado por OUI, classe do produto e serial. Quando a classe tem caracteres especiais, como o hífen de um modelo V5-V2, eles aparecem codificados no _id (%2D), e na URL o próprio % precisa ser codificado de novo (%252D). É a causa mais comum de 404 em integração. A projeção limita a resposta aos campos que você pediu.

Ler uma parte da árvore

Por desempenho, o ACS só busca da ONU o que algum provision declarou. Para descobrir o nome real de um parâmetro, peça a leitura da subárvore e depois consulte o dispositivo:

curl -i "$ACS/devices/$ONU/tasks?connection_request" -X POST --data '{"name": "refreshObject", "objectName": "InternetGatewayDevice.LANDevice.1.WLANConfiguration"}'

O ?connection_request faz o ACS chamar a ONU na hora. A resposta 200 quer dizer que a tarefa rodou; 202 quer dizer que ficou na fila para o próximo inform, em geral porque o connection request não alcançou a ONU.

Gravar um valor

curl -i "$ACS/devices/$ONU/tasks?connection_request" -X POST --data '{"name": "setParameterValues", "parameterValues": [["InternetGatewayDevice.LANDevice.1.WLANConfiguration.1.SSID", "CLI-0001"]]}'

Uma tarefa avulsa resolve o caso pontual, mas se o mesmo parâmetro também está num provision, o provision vence no próximo inform. Decida quem é o dono de cada parâmetro: ou o provision, ou a tarefa avulsa.

Proteger o GenieACS

O NBI não tem autenticação. Quem alcança a porta 7557 pode listar as ONUs, ler parâmetros, trocar configuração e mandar reset de fábrica para a base inteira. Proteger o ACS é tão importante quanto proteger a OLT.

  • NBI e interface web só no localhost. É o que fazem as variáveis GENIEACS_NBI_INTERFACE e GENIEACS_UI_INTERFACE. O acesso de fora passa por um proxy reverso com TLS e autenticação, ou por VPN.
  • Firewall de entrada. Abra o 7547 só para as faixas da rede de gestão das ONUs, e o 7567 só se você entrega firmware pelo ACS. O resto fica fechado.
  • Autenticação da ONU no ACS. Por padrão o GenieACS aceita qualquer equipamento. A chave cwmp.auth, em Admin e Config, permite exigir usuário e senha com a função AUTH().
  • Senha da interface web e segredo do JWT. Troque a senha do administrador no primeiro acesso e mantenha o arquivo de ambiente com permissão 600.

Erros comuns

  • ONU não aparece no GenieACS. Confira na OLT se a WAN de gestão tem IP (display ont wan-info), se o line profile tem o ip-index certo e se a ONU alcança a porta 7547. ONU offline na OLT nem chega a tentar; veja o guia de ONU com LOS ou offline.
  • Mudança no line profile que não pegou. Faltou commit, ou faltou reler o perfil depois. Sem save, a mudança some no próximo reboot da OLT.
  • Senha do Wi-Fi regravada em todo inform. O provision pede leitura nova de um campo que a ONU devolve em branco. Use {value: 1} nesses campos.

Para a parte de CLI usada aqui, como entrar em interface e ler estados, os comandos básicos da OLT Huawei MA5800 ajudam.

Perguntas frequentes

O que é o GenieACS?

É um servidor de auto-configuração (ACS) de código aberto que implementa o TR-069. Ele recebe as conexões das ONUs e roteadores, guarda o modelo de dados de cada um e aplica configurações por presets e provisions, além de oferecer uma API REST.

O GenieACS funciona com ONU de outros fabricantes?

Funciona com qualquer equipamento que fale TR-069. O que muda são os caminhos dos parâmetros, principalmente os específicos de cada fabricante, então cada modelo pede o seu preset, com a precondição pela classe do produto ou pelo fabricante.

Dá para configurar o PPPoE da ONU pelo GenieACS?

Dá, pelos parâmetros da conexão PPP, mas se a OLT também grava o PPPoE por OMCI você terá dois donos para o mesmo dado. Eu deixo o PPPoE com a OLT e uso o ACS para ler o login e configurar o Wi-Fi.

Por que a tarefa fica pendente e só roda horas depois?

Porque o connection request não alcançou a ONU, e a tarefa esperou o próximo inform periódico. Confira se o ACS tem rota até o IP da WAN de gestão e se nenhum firewall bloqueia esse acesso.

Conclusão

O GenieACS tira do atendimento a tarefa de entrar em cada ONU, mas só funciona com as duas pontas certas. Na OLT, a WAN de gestão com IP e o tr069-management apontando para ela, com commit, conferência e save. No servidor, presets com precondição bem definida, provisions idempotentes e um dono claro para cada parâmetro.

Comece pequeno: um modelo de ONU, um preset, um grupo de clientes. Confira os logs, ajuste o intervalo de inform e feche o NBI antes de ampliar. Com isso no lugar, trocar a senha do Wi-Fi vira uma chamada de API.

Compartilhar:

Sobre o autor

Ivan Alves

Especialista em redes de provedores (ISP)

Ivan Alves trabalha há mais de 15 anos com redes de provedores de internet, do NOC ao backbone e à borda BGP. Coordena infraestrutura e tecnologia em um provedor regional e escreve aqui os tutoriais que gostaria de ter encontrado quando começou: comandos testados, cenário explicado e o porquê de cada linha.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *