94 lines
2.6 KiB
PHP
94 lines
2.6 KiB
PHP
#!/usr/bin/php
|
|
<?php
|
|
function is_subnet_overlaps($subnet, $candidate)
|
|
{
|
|
$parts = explode("/", $subnet);
|
|
$prefix = $parts[1];
|
|
$subnet_first = ip2long($parts[0]);
|
|
$candidate_prefix = explode("/", $candidate)[1];
|
|
$count = pow(2, 32 - (int)$prefix);
|
|
$subnet_last = $subnet_first + $count - 1;
|
|
$test_ip = ip2long(explode("/", $candidate)[0]);
|
|
return $test_ip >= $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);
|