Quem precisa analisar rotas de rede e gargalos de latência no dia a dia sabe que rodar ferramentas de diagnóstico como o traceroute via terminal é essencial. No entanto, quando é preciso expor essa ferramenta em uma interface web para clientes ou equipes de suporte, a saída de texto puro nem sempre é a forma mais amigável de enxergar onde o pacote está perdendo tempo.
Para resolver isso, decidi construir uma interface web em PHP que executa o traceroute, destaca a latência de cada salto (hop) com cores dinâmicas e compila um fluxo visual: um grafo direcionado mostrando o trajeto exato até o destino.
Durante os testes, me deparei com uma dependência que costuma passar despercebida em servidores Linux mínimos: a ausência do binário responsável por compilar arquivos de grafos (DOT) em imagens bitmap.
Abaixo, compartilho a arquitetura da solução, o diagnóstico do erro e o código PHP completo.
1. A lógica da aplicação e o erro inicial#
O fluxo planejado para o script é simples:
- Receber o endereço IP ou hostname via formulário
POST. - Validar estritamente a entrada para evitar problemas de segurança.
- Executar o utilitário nativo
traceroute. - Tratar cada linha da saída: extrair número do salto, IP/domínio e tempos de resposta em milissegundos.
- Estruturar a rota em um arquivo na linguagem descritiva DOT.
- Invocar um comando no sistema operacional para compilar o arquivo
.dotem uma imagem.png.
O trecho de geração da imagem no PHP foi montado assim:
// Salvar a estrutura do grafo no disco
file_put_contents($dotFilePath, $dot);
// Compilar o arquivo DOT para imagem PNG
exec("dot -Tpng " . escapeshellarg($dotFilePath) . " -o " . escapeshellarg($imageFilePath));
Nos primeiros testes no servidor, a validação passava, o traceroute executava normalmente e o arquivo textual .dot era criado na pasta. Porém, a imagem PNG nunca era gravada e o script caía no aviso de falha:
Erro ao gerar o gráfico.
2. O diagnóstico: a dependência do Graphviz#
O motivo da falha é que a instrução dot -Tpng ... depende de um utilitário do sistema que não vem instalado por padrão em instalações limpas do Linux: o Graphviz.
O PHP consegue gerar a sintaxe da linguagem DOT sem qualquer biblioteca externa:
digraph G {
rankdir=LR;
"Inicio" -> "gateway.local (1.12 ms)";
"gateway.local (1.12 ms)" -> "10.0.0.1 (4.50 ms)";
"10.0.0.1 (4.50 ms)" -> "seusite.com.br (12.30 ms)";
}
No entanto, converter essa estrutura textual para um arquivo gráfico (.png ou .svg) exige o binário dot do Graphviz. Sem esse pacote presente no servidor, a função exec() encerra com código de erro e nenhuma imagem é produzida.
3. Instalando o Graphviz no servidor#
Para que o processo web (www-data, nginx ou nobody) consiga executar o compilador de grafos, instale o pacote correspondente à sua distribuição:
Em distribuições baseadas em Debian / Ubuntu:
sudo apt-get update
sudo apt-get install -y graphviz
Em distribuições baseadas em RHEL / AlmaLinux / Rocky Linux:
sudo dnf install -y graphviz
Assim que a instalação é concluída, o comando dot passa a ficar disponível no $PATH do sistema operacional.
4. O código PHP completo e tratado#
Abaixo está o script completo contendo validação de parâmetros, execução protegida, parse da saída do traceroute, destaque de cores por latência e renderização visual do grafo:
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// 1. Validacao estrita do Hostname ou IP recebido
function isValidHostname($hostname) {
return filter_var($hostname, FILTER_VALIDATE_IP) !== false ||
filter_var($hostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME);
}
$hostname = trim($_POST['hostname'] ?? '');
if (!isValidHostname($hostname)) {
echo "<div class='alert alert-warning'>Por favor, insira um IP ou hostname valido.</div>";
exit;
}
// 2. Execucao segura do traceroute
$command = escapeshellcmd('traceroute ' . $hostname);
$output = [];
$return_var = 0;
exec($command . ' 2>&1', $output, $return_var);
if ($return_var !== 0) {
$error_message = htmlspecialchars(implode("\n", $output));
echo "<div class='alert alert-danger'>Erro ao executar traceroute: <pre>$error_message</pre></div>";
} else {
$header = array_shift($output);
$formatted_output = '';
$lines = explode("\n", implode("\n", $output));
// 3. Inicializacao da estrutura do Grafo (Linguagem DOT)
$dot = "digraph G {\n";
$dot .= "rankdir=LR;\n"; // Disposicao horizontal (esquerda para a direita)
$previous_hop = "Inicio";
// Sanitizacao para o nome dos arquivos temporarios
$dotname = preg_replace('/[^a-zA-Z0-9_-]/', '_', $hostname);
foreach ($lines as $line) {
$line = trim($line);
if (empty($line) || strpos($line, 'traceroute') !== false) {
continue;
}
$parts = preg_split('/\s+/', $line);
// Verifica se a linha comeca com o numero do salto
if (isset($parts[0]) && is_numeric($parts[0])) {
$hop_number = $parts[0];
$hop_name = $parts[1] ?? "Desconhecido";
$latencies = [];
// Captura os tempos de resposta em milissegundos
foreach ($parts as $part) {
if (preg_match('/(\d+\.\d+)\s*ms/', $part, $matches)) {
$latencies[] = $matches[1] . " ms";
}
}
$latency_str = implode(", ", $latencies);
$node_label = "{$hop_name} ({$latency_str})";
// Conecta o no anterior ao no atual no grafo
$dot .= "\"{$previous_hop}\" -> \"{$node_label}\";\n";
$previous_hop = $node_label;
unset($parts[0]);
$line_content = implode(' ', $parts);
$formatted_line = "<div class='__traceroute__value'><span class='__traceroute_route'>{$hop_number}</span> {$line_content}</div>";
} else {
$formatted_line = "<div class='__traceroute__value'>{$line}</div>";
}
// Destaque visual por cores dependendo do RTT
$formatted_line = preg_replace_callback('/(\d+\.\d+)\s+ms/', function($matches) {
$ms = floatval($matches[1]);
$color_class = 'default';
if ($ms < 1) {
$color_class = 'green';
} elseif ($ms < 5) {
$color_class = 'blue';
} else {
$color_class = 'red';
}
return "<span class='latency {$color_class}'>{$matches[0]}</span>";
}, $formatted_line);
$formatted_output .= $formatted_line;
}
$dot .= "}\n";
// 4. Definicao dos caminhos de armazenamento
$storageDir = '/var/www/html/views/tools/traceroute/';
$dotFilePath = $storageDir . $dotname . '_traceroute_graph.dot';
$imageFilePath = $storageDir . $dotname . '_traceroute.png';
file_put_contents($dotFilePath, $dot);
// 5. Compilacao da imagem com Graphviz
exec("dot -Tpng " . escapeshellarg($dotFilePath) . " -o " . escapeshellarg($imageFilePath));
// 6. Exibicao no navegador
if (file_exists($imageFilePath)) {
echo "<div class='alert alert-info'>" . htmlspecialchars($header) . "</div>";
echo "<div class='__traceroute__container'><div class='__traceroute__content'>{$formatted_output}</div></div>";
echo "<div class='mt-4'><div class='__traceroute__image'><img src='views/tools/traceroute/{$dotname}_traceroute.png' alt='Traceroute Graph para {$dotname}'></div></div>";
} else {
echo "<div class='alert alert-danger'>Erro ao gerar o grafico.</div>";
}
}
} else {
echo "<div class='alert alert-danger'>Solicitacao invalida.</div>";
}
?>
Boas práticas para ferramentas de diagnóstico web#
Integrar ferramentas de terminal com PHP enriquece bastante a experiência de diagnóstico de rede. No entanto, sempre que utilizar funções de execução de comandos (exec, shell_exec), lembre-se de:
- Sanitizar rigorosamente entradas: Utilizar filtros nativos de IP/domínio e funções como
escapeshellarg()para impedir injeção de comandos arbitrários no servidor. - Mapear dependências externas: Documentar e incluir utilitários do sistema (como o Graphviz) nos seus playbooks de provisionamento para evitar falhas silenciosas na produção.
Este artigo foi útil?
Deixe uma reação rápida para apoiar o conteúdo:
Este post está licenciado sob CC BY-NC.



Comentários
Participe da discussão abaixo.
0 comentários