#!/usr/bin/php = $subnet_first and $test_ip <= $subnet_last and $candidate_prefix > $prefix; } class RoutingTableReader { private $routes = []; /** * @return array */ public function getRoutes(): array { return $this->routes; } public function __construct() { $result = @shell_exec("ip --json route show"); if (!$result) { throw new RuntimeException("Failed to read routing table"); } $this->routes = @json_decode($result, true); if ($this->routes === null) { throw new RuntimeException("Failed to parse json output"); } foreach ($this->routes as $key => &$route) { if ($route["dst"] === "default") { $route["dst"] = "0.0.0.0/0"; } elseif (!str_contains($route["dst"], "/")) { $route["dst"] .= "/32"; } } } } try { if (!isset($argv[1])) { throw new RuntimeException("Output config is not set"); } $config = []; $reader = new RoutingTableReader(); $routes = []; $overlapped = []; foreach ($reader->getRoutes() as $route) { if ($route["dst"] == "0.0.0.0/0") { continue; } $routes[] = $route; } foreach ($routes as $i => $master) { if (in_array($master, $overlapped)) { continue; } foreach ($routes as $j => $slave) { if ($i == $j or in_array($slave, $overlapped)) { continue; } if (is_subnet_overlaps($master["dst"], $slave["dst"])) { $overlapped[] = $slave; } } } $subnets = []; foreach ($routes as $i => $master) { if (in_array($master, $overlapped)) { continue; } $subnets[] = $master["dst"]; } foreach ($subnets as $route) { $parts = explode("/", $route); $mask = long2ip(-1 << (32 - (int)$parts[1])); $dst = $parts[0]; $config[] = "push \"route {$dst} {$mask}\""; } $outfile = $argv[1]; file_put_contents($outfile, implode("\n", $config)); } catch (Exception $e) { echo "\nError:" . $e->getMessage() . "\n"; exit(1); } exit(0);