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

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
- Uma WAN de gestão na ONU, entregue pela OLT por OMCI, com IP que alcance o ACS (no exemplo, DHCP na VLAN 40).
- O
tr069-managementligado no line profile e apontando para essa WAN comip-index. - A ONU sabendo o endereço do ACS e o ACS escutando na porta 7547 (CWMP).
- No GenieACS, um preset que diz quais ONUs recebem a configuração e um provision que diz o que configurar.
- 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
| Evento | Quando aparece | O que o ACS costuma fazer |
|---|---|---|
| 0 BOOTSTRAP | Primeiro contato com este ACS, ou depois de reset de fábrica | Limpar o cache do modelo de dados e aplicar a configuração inicial |
| 2 PERIODIC | Chegou a hora do inform periódico | Aplicar o que estiver pendente |
| 6 CONNECTION REQUEST | A sessão foi pedida pelo ACS | Executar a tarefa que motivou o pedido |
| 7 TRANSFER COMPLETE | Terminou um download (firmware ou arquivo) | Reiniciar ou validar a versão |

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ço | Porta padrão | Função |
|---|---|---|
| genieacs-cwmp | 7547 | Recebe as ONUs (TR-069). Precisa ficar acessível pela rede de gestão. |
| genieacs-nbi | 7557 | API REST para o seu sistema. Sem autenticação. |
| genieacs-fs | 7567 | Entrega arquivos (firmware) para as ONUs. |
| genieacs-ui | 3000 | Interface web, com login. |
| MongoDB | 27017 | Banco 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 $ENVUnidades 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_INTERFACEeGENIEACS_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çãoAUTH(). - 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 oip-indexcerto 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. Semsave, 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.
