diff --git a/src/Commands/GenDuid.php b/src/Commands/GenDuid.php index 082a460..0260475 100644 --- a/src/Commands/GenDuid.php +++ b/src/Commands/GenDuid.php @@ -40,12 +40,13 @@ final class GenDuid extends Command $manufacturer = '001E46'; } - $question = new Question('Serial number?'); + $question = new Question('Serial number: '); /** @var string $serial */ $serial = $helper->ask($input, $output, $question); - $output->writeln('Identifier: ' . $this->ascii2hex($manufacturer . ':' . $serial)); - $output->write($this->getInstructions()); + $id = $this->ascii2hex($manufacturer . ':' . $serial); + $output->writeln('Identifier: ' . $id); + $output->write($this->getInstructions($id)); return Command::SUCCESS; } @@ -60,7 +61,7 @@ final class GenDuid extends Command return implode(':', $bytes); } - private function getInstructions(): string + private function getInstructions($id): string { return <<config = $config; $this->logger = $logger; $this->ngControl = $ngControl; + $this->ifConfig = $ifConfig; } /** @@ -43,6 +47,7 @@ final class Monitor extends Command $this->logger->info('Stopping 5268AC ping monitor ...'); }); + $failures = 0; while ($input->isInteractive()) { if (!$this->ping()) { if ($this->ngControl->ngIsConnected()) { @@ -52,12 +57,38 @@ final class Monitor extends Command else { $this->logger->info('Everything is going well.'); } + $failures = 0; } elseif (!$this->ngControl->ngIsConnected()) { $this->logger->warning('Connection to ' . $this->config->getPingHost() . ' is down, but EAP is not being bridged!'); $this->ngControl->ngConnect(); } else { - $this->logger->error('Connection to ' . $this->config->getPingHost() . ' is down, and EAP is not being bridged! We are stuck!'); + $failures++; + if ($failures > 10 ){ + $this->logger->error('Connection to ' . $this->config->getPingHost() . ' is down, and EAP is bridged! We are stuck!'); + } + elseif ($failures > 3) { + $this->logger->error('Things are stuck. Lets trying restarting things.'); + $this->ifConfig->stop( + $this->config->getOntInterface(), + $this->config->getRgInterface() + ); + $this->ngControl->removeNodes( + $this->config->getOntInterface(), + $this->config->getRgInterface() + ); + sleep(2); + $this->logger->error('Bringing things back up.'); + $this->ngControl->createNodes( + $this->config->getOntInterface(), + $this->config->getRgInterface(), + $this->config->getRgEthernetMac() + ); + $this->ifConfig->start( + $this->config->getOntInterface(), + $this->config->getRgInterface() + ); + } } sleep(5); } diff --git a/src/Commands/Restart.php b/src/Commands/Restart.php new file mode 100644 index 0000000..1d9e88e --- /dev/null +++ b/src/Commands/Restart.php @@ -0,0 +1,34 @@ +getApplication(); + if (!$application) { + return Command::FAILURE; + } + $application->find('shutdown') + ->run(new ArrayInput([]), $output); + $application->find('startup') + ->run(new ArrayInput([]), $output); + return Command::SUCCESS; + } +} diff --git a/src/Commands/Shutdown.php b/src/Commands/Shutdown.php new file mode 100644 index 0000000..47c491a --- /dev/null +++ b/src/Commands/Shutdown.php @@ -0,0 +1,48 @@ +config = $config; + $this->logger = $logger; + $this->ngControl = $ngControl; + } + + /** + * {@inheritDoc} + */ + protected function execute( + InputInterface $input, + OutputInterface $output + ): int { + $this->ngControl->removeNodes( + $this->config->getOntInterface(), + $this->config->getRgInterface() + ); + return Command::SUCCESS; + } +} diff --git a/src/Commands/Startup.php b/src/Commands/Startup.php index 75b6f01..3c0d475 100644 --- a/src/Commands/Startup.php +++ b/src/Commands/Startup.php @@ -5,7 +5,9 @@ declare(strict_types=1); namespace Pfatt\Commands; use Pfatt\Service\Config; -use Pfatt\Service\NgController; +use Pfatt\Service\IfConfig; +use Pfatt\Service\KldLoad; +use Pfatt\Service\NgCtl; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; @@ -15,42 +17,44 @@ use Symfony\Component\Process\Process; final class Startup extends Command { protected static $defaultName = 'startup'; - protected static $defaultDescription = 'Setup initial connection'; + protected static $defaultDescription = 'Start netgraph connetion'; protected Config $config; protected LoggerInterface $logger; - protected NgController $ngControl; + protected NgCtl $ngControl; + private IfConfig $ifConfig; + private KldLoad $kldLoad; public function __construct( - Config $config, + Config $config, LoggerInterface $logger, - NgController $ngControl + NgCtl $ngControl, + IfConfig $ifConfig, + KldLoad $kldLoad ) { parent::__construct('startup'); $this->config = $config; $this->logger = $logger; $this->ngControl = $ngControl; + $this->ifConfig = $ifConfig; + $this->kldLoad = $kldLoad; } /** * {@inheritDoc} */ protected function execute( - InputInterface $input, - OutputInterface $output + InputInterface $input, + OutputInterface $output ): int { - switch ($this->getVeriant()) { + switch ($this->getVariant()) { case 'opnsense': - $kldload = function (string $mod): Process { - return new Process(['/sbin/kldload', '-nq', $mod]); - }; - $this->logger->info('loading netgraph kernel modules...'); - $kldload('netgraph')->mustRun(); - $kldload('ng_ether')->mustRun(); - $kldload('ng_etf')->mustRun(); - $kldload('ng_vlan')->mustRun(); - $kldload('ng_eiface')->mustRun(); - $kldload('ng_one2many')->mustRun(); + $this->kldLoad->execute('netgraph'); + $this->kldLoad->execute('ng_ether'); + $this->kldLoad->execute('ng_etf'); + $this->kldLoad->execute('ng_vlan'); + $this->kldLoad->execute('ng_eiface'); + $this->kldLoad->execute('ng_one2many'); $this->logger->info('OK!'); break; @@ -58,9 +62,9 @@ final class Startup extends Command default: $attach = function (string $interface): Process { return new Process([ - '/usr/local/bin/php', - '-r', - '"pfSense_ngctl_attach(\'.\', \'' . $interface . '\';"' + '/usr/local/bin/php', + '-r', + '"pfSense_ngctl_attach(\'.\', \'' . $interface . '\');"' ]); }; @@ -71,17 +75,23 @@ final class Startup extends Command break; } $this->ngControl->createNodes( - $this->config->getOntInterface(), - $this->config->getRgInterface(), - $this->config->getRgEthernetMac() + $this->config->getOntInterface(), + $this->config->getRgInterface(), + $this->config->getRgEthernetMac() ); + $this->ifConfig->start( + $this->config->getOntInterface(), + $this->config->getRgInterface() + ); + $this->logger->info("ngeth0 should now be available to configure as your pfSense WAN"); + $this->logger->info("Done"); return Command::SUCCESS; } /** * @return string */ - private function getVeriant(): string + private function getVariant(): string { // @todo detect the variant to trigger different behaviors. return 'opnsense'; diff --git a/src/PfattKernel.php b/src/PfattKernel.php index 7454905..7c8fbcc 100644 --- a/src/PfattKernel.php +++ b/src/PfattKernel.php @@ -9,9 +9,13 @@ use Monolog\Logger; use Monolog\Handler\StreamHandler; use Pfatt\Commands\GenDuid; use Pfatt\Commands\Monitor; +use Pfatt\Commands\Restart; +use Pfatt\Commands\Shutdown; use Pfatt\Commands\Startup; use Pfatt\Service\Config; -use Pfatt\Service\NgController; +use Pfatt\Service\IfConfig; +use Pfatt\Service\KldLoad; +use Pfatt\Service\NgCtl; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Application; use Symfony\Component\Console\CommandLoader\ContainerCommandLoader; @@ -46,38 +50,38 @@ final class PfattKernel if (!file_exists($file)) { $containerBuilder = new ContainerBuilder(); - $containerBuilder->register(Config::class, Config::class) - ->setPublic(true); + $containerBuilder->register(Config::class, Config::class); // Setup various logging handlers. $containerBuilder->register('syslogger', SyslogHandler::class) - ->setArguments(['pfatt']) - ->setPublic(true); + ->setArguments(['pfatt']); $containerBuilder->register(LoggerInterface::class, Logger::class) - ->setArguments(['pfatt', [new Reference('syslogger')]]) - ->setPublic(true); + ->setArguments(['pfatt', [new Reference('syslogger')]]); $containerBuilder->setAlias(Logger::class, LoggerInterface::class); - // Utilities. - $containerBuilder->autowire(NgController::class, NgController::class) - ->setPublic(true); + $containerBuilder->autowire(NgCtl::class, NgCtl::class); + $containerBuilder->autowire(KldLoad::class, KldLoad::class); + $containerBuilder->autowire(IfConfig::class, IfConfig::class); // Register commands. $containerBuilder->autowire(Startup::class, Startup::class) - ->setPublic(true); + ->setPublic(true); + $containerBuilder->autowire(Shutdown::class, Shutdown::class) + ->setPublic(true); + $containerBuilder->autowire(Restart::class, Restart::class) + ->setPublic(true); $containerBuilder->autowire(GenDuid::class, GenDuid::class) - ->setPublic(true); + ->setPublic(true); $containerBuilder->autowire(Monitor::class, Monitor::class) - ->setPublic(true); + ->setPublic(true); // Register application. $containerBuilder->register(ContainerCommandLoader::class, ContainerCommandLoader::class) ->setArguments([ new Reference('service_container'), $this->commands - ]) - ->setPublic(true); + ]); $containerBuilder->autowire(PfattApplication::class, PfattApplication::class) ->addMethodCall('setCommandLoader', [new Reference(ContainerCommandLoader::class)]) ->setPublic(true); diff --git a/src/Service/IfConfig.php b/src/Service/IfConfig.php new file mode 100644 index 0000000..907db67 --- /dev/null +++ b/src/Service/IfConfig.php @@ -0,0 +1,57 @@ +logger = $logger; + } + + public function start(string $ont, string $rg): void + { + $this->logger->info(" enabling $rg interface..."); + $this->exec([$rg, 'up']); + $this->logger->info('OK!'); + + $this->logger->info(" enabling $ont interface..."); + $this->exec([$ont, 'up"']); + $this->logger->info('OK!'); + + $this->logger->info(" enabling promiscuous mode on $rg..."); + $this->exec([$rg, 'promisc']); + $this->logger->info('OK!'); + + $this->logger->info(" enabling promiscuous mode on $ont..."); + $this->exec([$ont, 'promisc']); + $this->logger->info('OK!'); + } + + public function stop(string $ont, string $rg): void + { + // + } + + /** + * Run a ifconfig command. + * + * @param array $args + * Additional arguments to pass to ifconfig. + * + * @return bool + * True on success, false on failure. + */ + private function exec(array $args): bool + { + $process = new Process(['/sbin/ifconfig', ...$args], '/tmp'); + return !$process->run(); + } +} diff --git a/src/Service/KldLoad.php b/src/Service/KldLoad.php new file mode 100644 index 0000000..df1213b --- /dev/null +++ b/src/Service/KldLoad.php @@ -0,0 +1,25 @@ +run(); + } +} diff --git a/src/Service/NgController.php b/src/Service/NgCtl.php similarity index 66% rename from src/Service/NgController.php rename to src/Service/NgCtl.php index 6131aeb..60f4441 100644 --- a/src/Service/NgController.php +++ b/src/Service/NgCtl.php @@ -8,7 +8,7 @@ use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Process\Process; -class NgController implements LoggerAwareInterface +class NgCtl implements LoggerAwareInterface { private LoggerInterface $logger; @@ -22,7 +22,7 @@ class NgController implements LoggerAwareInterface */ public function ngIsConnected(): bool { - return $this->ngCtlRun(['show', 'laneapfilter:eapout']); + return $this->exec(['show', 'laneapfilter:eapout']); } /** @@ -31,7 +31,7 @@ class NgController implements LoggerAwareInterface public function ngRmHook(): void { $this->logger->info('Disconnecting netgraph node ...'); - $this->ngCtlRun(['rmhook', 'laneapfilter: eapout']) ? + $this->exec(['rmhook', 'laneapfilter:', 'eapout']) ? $this->logger->info('OK!') : $this->logger->error('ERROR!'); } @@ -42,7 +42,7 @@ class NgController implements LoggerAwareInterface public function ngConnect(): void { $this->logger->info('Connecting netgraph node ...'); - $this->ngCtlRun(['connect', 'waneapfilter:', 'laneapfilter:', 'eapout', 'eapout']) ? + $this->exec(['connect', 'waneapfilter:', 'laneapfilter:', 'eapout', 'eapout']) ? $this->logger->info('OK!') : $this->logger->error('ERROR!'); } @@ -63,27 +63,26 @@ class NgController implements LoggerAwareInterface $this->logger->info('Building netgraph nodes.'); $this->logger->info(' creating ng_one2many...'); - $this->ngCtlRun(['mkpeer', $ont . ':', 'one2many', 'lower', 'one']); - $this->ngCtlRun(['name', $ont . ':lower', 'o2m']); + $this->exec(['mkpeer', $ont . ':', 'one2many', 'lower', 'one']); + $this->exec(['name', $ont . ':lower', 'o2m']); $this->logger->info('OK!'); $this->logger->info(' creating vlan node and interface...'); - $this->ngCtlRun(['mkpeer', 'o2m:', 'vlan', 'many0', ' downstream']); - $this->ngCtlRun(['name', 'o2m:many0', 'vlan0']); - $this->ngCtlRun(['mkpeer', 'vlan0:', 'eiface', 'vlan0', 'ether']); - - $this->ngCtlRun([ + $this->exec(['mkpeer', 'o2m:', 'vlan', 'many0', ' downstream']); + $this->exec(['name', 'o2m:many0', 'vlan0']); + $this->exec(['mkpeer', 'vlan0:', 'eiface', 'vlan0', 'ether']); + $this->exec([ 'msg', 'vlan0:', '\'addfilter { vlan=0 hook="vlan0" }\'' ]); - $this->ngCtlRun(['msg', 'ngeth0:', 'set', $rgMac]); + $this->exec(['msg', 'ngeth0:', 'set', $rgMac]); $this->logger->info('OK!'); $this->logger->info(" defining etf for $ont (ONT)..."); - $this->ngCtlRun(['mkpeer', 'o2m:', 'etf', 'many1', 'downstream']); - $this->ngCtlRun(['name', 'o2m:many1', 'waneapfilter']); - $this->ngCtlRun([ + $this->exec(['mkpeer', 'o2m:', 'etf', 'many1', 'downstream']); + $this->exec(['name', 'o2m:many1', 'waneapfilter']); + $this->exec([ 'connect', 'waneapfilter:', $ont . ':', @@ -93,9 +92,9 @@ class NgController implements LoggerAwareInterface $this->logger->info('OK!'); $this->logger->info(" defining etf for $rg (RG)..."); - $this->ngCtlRun(['mkpeer', $rg . ':', 'etf', 'lower', 'downstream']); - $this->ngCtlRun(['name', $rg . ':', 'lower', 'laneapfilter']); - $this->ngCtlRun([ + $this->exec(['mkpeer', $rg . ':', 'etf', 'lower', 'downstream']); + $this->exec(['name', $rg . ':', 'lower', 'laneapfilter']); + $this->exec([ 'connect', 'laneapfilter:', $rg . ':', @@ -105,7 +104,7 @@ class NgController implements LoggerAwareInterface $this->logger->info('OK!'); $this->logger->info(" bridging etf for $ont <-> $rg..."); - $this->ngCtlRun([ + $this->exec([ 'connect', 'waneapfilter:', 'laneapfilter:', @@ -115,12 +114,12 @@ class NgController implements LoggerAwareInterface $this->logger->info('OK!'); $this->logger->info(" defining filters for EAP traffic..."); - $this->ngCtlRun([ + $this->exec([ 'msg', 'waneapfilter:', '\'setfilter { matchhook="eapout" ethertype=0x888e }\'' ]); - $this->ngCtlRun([ + $this->exec([ 'msg', 'laneapfilter:', '\'setfilter { matchhook="eapout" ethertype=0x888e }\'' @@ -128,7 +127,7 @@ class NgController implements LoggerAwareInterface $this->logger->info('OK!'); $this->logger->info(" enabling one2many links..."); - $this->ngCtlRun([ + $this->exec([ 'msg', 'o2m:', 'setconfig', @@ -137,23 +136,19 @@ class NgController implements LoggerAwareInterface $this->logger->info('OK!'); $this->logger->info(" removing waneapfilter:nomatch hook..."); - $this->ngCtlRun(['rmhook', 'waneapfilter:', 'nomatch']); + $this->exec(['rmhook', 'waneapfilter:', 'nomatch']); $this->logger->info('OK!'); + } - $this->logger->info(" enabling $rg interface..."); - $this->ngCtlRun([$rg, 'up']); - $this->logger->info('OK!'); - - $this->logger->info(" enabling $ont interface..."); - $this->ngCtlRun([$ont, 'up"']); - $this->logger->info('OK!'); - - $this->logger->info(" enabling promiscuous mode on $rg..."); - $this->ngCtlRun([$rg, 'promisc']); - $this->logger->info('OK!'); - - $this->logger->info("ngeth0 should now be available to configure as your pfSense WAN"); - $this->logger->info("Done"); + public function removeNodes(string $ont, string $rg): void + { + $this->exec(['shutdown', 'waneapfilter:']); + $this->exec(['shutdown', 'laneapfilter:']); + $this->exec(['shutdown', $ont . ':']); + $this->exec(['shutdown', $rg . ':']); + $this->exec(['shutdown', 'o2m:']); + $this->exec(['shutdown', 'vlan0:']); + $this->exec(['shutdown', 'ngeth0:']); } /** @@ -165,7 +160,7 @@ class NgController implements LoggerAwareInterface * @return bool * True on success, false on failure. */ - private function ngCtlRun(array $args): bool + private function exec(array $args): bool { $process = new Process(['/usr/sbin/ngctl', ...$args], '/tmp'); return !$process->run();