diff --git a/box.json.dist b/box.json.dist index c390835..f174cd7 100644 --- a/box.json.dist +++ b/box.json.dist @@ -3,5 +3,6 @@ "files": [ "bin/pfatt", "cache/container.php" - ] + ], + "compression": "GZ" } diff --git a/composer.json b/composer.json index d021cea..e0b403e 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ ], "require": { "php": ">=7.4", + "monolog/monolog": "^2.3", "psr/log": "^1.1", "symfony/config": "^5.4", "symfony/console": "^5.4", @@ -25,6 +26,7 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.5", "phpstan/phpstan": "^1.4", + "squizlabs/php_codesniffer": "^3.6", "vimeo/psalm": "^4.22" }, "bin": [ diff --git a/src/Commands/Monitor.php b/src/Commands/Monitor.php index 76f0456..05fbd7d 100644 --- a/src/Commands/Monitor.php +++ b/src/Commands/Monitor.php @@ -5,8 +5,8 @@ declare(strict_types=1); namespace Pfatt\Commands; use Pfatt\Service\Config; -use Pfatt\Service\Logger; use Pfatt\Service\NgController; +use Psr\Log\LoggerInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -17,12 +17,12 @@ final class Monitor extends Command protected static $defaultName = 'monitor'; protected static $defaultDescription = 'Monitor connection to trigger 5268AC updates'; protected Config $config; - protected Logger $logger; + protected LoggerInterface $logger; protected NgController $ngControl; public function __construct( Config $config, - Logger $logger, + LoggerInterface $logger, NgController $ngControl ) { parent::__construct('monitor'); @@ -34,20 +34,31 @@ final class Monitor extends Command /** * {@inheritDoc} */ - protected function execute(InputInterface $input, OutputInterface $output): int - { - $this->logger->setOutput($output); - + protected function execute( + InputInterface $input, + OutputInterface $output + ): int { $this->logger->info('Starting 5268AC ping monitor ...'); - register_shutdown_function(function () { $this->logger->info('Stopping 5268AC ping monitor ...'); }); + register_shutdown_function(function () { + $this->logger->info('Stopping 5268AC ping monitor ...'); + }); while ($input->isInteractive()) { if (!$this->ping()) { - $this->ngControl->ngRmHook(); - } - elseif (!$this->ngControl->ngIsConnected()) { + if ($this->ngControl->ngIsConnected()) { + $this->logger->warning('Connection to ' . $this->config->getPingHost() . ' is up, but EAP is being bridged!'); + $this->ngControl->ngRmHook(); + } + else { + $this->logger->info('Everything is going well.'); + } + } 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!'); + } sleep(5); } return Command::SUCCESS; @@ -56,11 +67,11 @@ final class Monitor extends Command private function ping(): bool { $process = new Process([ - '/bin/ping', - '-t2', - '-q', - '-c1', - $this->config->getPingHost() + '/sbin/ping', + '-t2', + '-q', + '-c1', + $this->config->getPingHost() ]); return (bool) $process->run(); } diff --git a/src/Commands/Startup.php b/src/Commands/Startup.php index 920147d..75b6f01 100644 --- a/src/Commands/Startup.php +++ b/src/Commands/Startup.php @@ -5,8 +5,8 @@ declare(strict_types=1); namespace Pfatt\Commands; use Pfatt\Service\Config; -use Pfatt\Service\Logger; use Pfatt\Service\NgController; +use Psr\Log\LoggerInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -17,12 +17,12 @@ final class Startup extends Command protected static $defaultName = 'startup'; protected static $defaultDescription = 'Setup initial connection'; protected Config $config; - protected Logger $logger; + protected LoggerInterface $logger; protected NgController $ngControl; public function __construct( Config $config, - Logger $logger, + LoggerInterface $logger, NgController $ngControl ) { parent::__construct('startup'); @@ -38,8 +38,6 @@ final class Startup extends Command InputInterface $input, OutputInterface $output ): int { - $this->logger->setOutput($output); - switch ($this->getVeriant()) { case 'opnsense': $kldload = function (string $mod): Process { diff --git a/src/PfattApplication.php b/src/PfattApplication.php index dcec7b6..3bb10cc 100644 --- a/src/PfattApplication.php +++ b/src/PfattApplication.php @@ -4,8 +4,51 @@ declare(strict_types=1); namespace Pfatt; +use Monolog\Formatter\LineFormatter; +use Monolog\Handler\PsrHandler; +use Monolog\Logger; use Symfony\Component\Console\Application; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Logger\ConsoleLogger; +use Symfony\Component\Console\Output\OutputInterface; final class PfattApplication extends Application { + private Logger $logger; + + public function __construct(Logger $logger) + { + parent::__construct('PfATT', '1.0'); + $this->logger = $logger; + } + + protected function getDefaultInputDefinition() + { + $definition = parent::getDefaultInputDefinition(); + $definition->addOption(new InputOption( + '--debug', + '-d', + InputOption::VALUE_NONE, + 'Trigger debug output' + )); + return $definition; + } + + protected function doRunCommand( + Command $command, + InputInterface $input, + OutputInterface $output + ) { + // We don't want to interact with the command execution but this is the + // only place to interact with input after the definition is bound and + // before the command is run. + if ($input->hasOption('debug')) { + $handler = new PsrHandler(new ConsoleLogger($output)); + $handler->setFormatter(new LineFormatter('[%datetime%] %channel%.%level_name%: %message%')); + $this->logger->pushHandler($handler); + } + return parent::doRunCommand($command, $input, $output); + } } diff --git a/src/PfattKernel.php b/src/PfattKernel.php index 8b7ac6a..7454905 100644 --- a/src/PfattKernel.php +++ b/src/PfattKernel.php @@ -4,11 +4,13 @@ declare(strict_types=1); namespace Pfatt; +use Monolog\Handler\SyslogHandler; +use Monolog\Logger; +use Monolog\Handler\StreamHandler; use Pfatt\Commands\GenDuid; use Pfatt\Commands\Monitor; use Pfatt\Commands\Startup; use Pfatt\Service\Config; -use Pfatt\Service\Logger; use Pfatt\Service\NgController; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Application; @@ -44,15 +46,20 @@ final class PfattKernel if (!file_exists($file)) { $containerBuilder = new ContainerBuilder(); - $containerBuilder->register(Logger::class, Logger::class) - ->setPublic(true); - $containerBuilder->setAlias(LoggerInterface::class, Logger::class); - $containerBuilder->register('logger-5268', Logger::class) - ->setPublic(true) - ->setArgument('$channel', 'pfatt-5268AC'); $containerBuilder->register(Config::class, Config::class) - ->setPublic(true) - ->setArguments(['', '', '']); + ->setPublic(true); + + // Setup various logging handlers. + $containerBuilder->register('syslogger', SyslogHandler::class) + ->setArguments(['pfatt']) + ->setPublic(true); + $containerBuilder->register(LoggerInterface::class, Logger::class) + ->setArguments(['pfatt', [new Reference('syslogger')]]) + ->setPublic(true); + $containerBuilder->setAlias(Logger::class, LoggerInterface::class); + + + // Utilities. $containerBuilder->autowire(NgController::class, NgController::class) ->setPublic(true); @@ -62,8 +69,7 @@ final class PfattKernel $containerBuilder->autowire(GenDuid::class, GenDuid::class) ->setPublic(true); $containerBuilder->autowire(Monitor::class, Monitor::class) - ->setPublic(true) - ->setArgument(Logger::class, new Reference('logger-5268')); + ->setPublic(true); // Register application. $containerBuilder->register(ContainerCommandLoader::class, ContainerCommandLoader::class) @@ -72,8 +78,7 @@ final class PfattKernel $this->commands ]) ->setPublic(true); - $containerBuilder->register(PfattApplication::class, PfattApplication::class) - ->setArguments(['PfATT']) + $containerBuilder->autowire(PfattApplication::class, PfattApplication::class) ->addMethodCall('setCommandLoader', [new Reference(ContainerCommandLoader::class)]) ->setPublic(true); @@ -96,5 +101,4 @@ final class PfattKernel $container = new \ProjectServiceContainer(); return $container; } - } diff --git a/src/Service/Logger.php b/src/Service/Logger.php deleted file mode 100644 index bc01257..0000000 --- a/src/Service/Logger.php +++ /dev/null @@ -1,63 +0,0 @@ -channel = $channel; - $this->logger = $logger; - } - - /** - * {@inheritDoc} - */ - public function log($level, $message, array $context = array()) - { - $full_message = $this->getTimestamp() . ' :: [' . $this->channel . '] :: ' . $message; - if (isset($this->logger)) { - $this->logger->log($level, $full_message, $context); - } - else { - echo $full_message; - } - } - - /** - * Get the current timestamp formatted for logs. - * - * @return string - * ISO8601 formatted timestamp. - */ - private function getTimestamp(): string - { - return (new \DateTime())->format(\DateTimeInterface::ISO8601); - } - - /** - * Add output to use for writing. - * - * @param \Symfony\Component\Console\Output\OutputInterface $output - * Output service. - */ - public function setOutput(OutputInterface $output): void { - $this->logger = new ConsoleLogger($output); - } -} diff --git a/src/Service/NgController.php b/src/Service/NgController.php index c35f189..6131aeb 100644 --- a/src/Service/NgController.php +++ b/src/Service/NgController.php @@ -22,29 +22,29 @@ class NgController implements LoggerAwareInterface */ public function ngIsConnected(): bool { - return $this->ngCtlRun('show laneapfilter:eapout'); + return $this->ngCtlRun(['show', 'laneapfilter:eapout']); } /** - * Use ngctl remove connection. + * Remove the eapout connection between laneapfilter and waneapfilter. */ public function ngRmHook(): void { $this->logger->info('Disconnecting netgraph node ...'); - $this->ngCtlRun('rmhook laneapfilter: eapout') ? - $this->logger->info('OK!') : - $this->logger->error('ERROR!'); + $this->ngCtlRun(['rmhook', 'laneapfilter: eapout']) ? + $this->logger->info('OK!') : + $this->logger->error('ERROR!'); } /** - * Use ngctl to establish connection. + * Connect laneapfilter and waneapfilter with eapout. */ public function ngConnect(): void { $this->logger->info('Connecting netgraph node ...'); - $this->ngCtlRun('connect waneapfilter: laneapfilter: eapout eapout') ? - $this->logger->info('OK!') : - $this->logger->error('ERROR!'); + $this->ngCtlRun(['connect', 'waneapfilter:', 'laneapfilter:', 'eapout', 'eapout']) ? + $this->logger->info('OK!') : + $this->logger->error('ERROR!'); } /** @@ -54,157 +54,121 @@ class NgController implements LoggerAwareInterface * ONT network connection interface. * @param string $rg * Residential Gateway network connection interface. - * @return int|void - * ... - * @todo are all the ifs reversed? - * + * @param string $rgMac + * Residential Gateway MAC address. + * @return void */ - public function createNodes(string $ont, string $rg, string $rgMac) + public function createNodes(string $ont, string $rg, string $rgMac): void { $this->logger->info('Building netgraph nodes.'); $this->logger->info(' creating ng_one2many...'); - if ( - $this->ngCtlRun("mkpeer $ont: one2many lower one") - && $this->ngCtlRun("name $ont:lower o2m") - ) { - $this->logger->info('OK!'); - } else { - $this->logger->error('ERROR!'); - return 1; - } + $this->ngCtlRun(['mkpeer', $ont . ':', 'one2many', 'lower', 'one']); + $this->ngCtlRun(['name', $ont . ':lower', 'o2m']); + $this->logger->info('OK!'); $this->logger->info(' creating vlan node and interface...'); - if ( - $this->ngCtlRun('mkpeer o2m: vlan many0 downstream') - && $this->ngCtlRun('name o2m:many0 vlan0') - && $this->ngCtlRun('mkpeer vlan0: eiface vlan0 ether') - && $this->ngCtlRun('msg vlan0: \'addfilter { vlan=0 hook="vlan0" }\'') - && $this->ngCtlRun("msg ngeth0: set $rgMac") - ) { - $this->logger->info('OK!'); - } else { - $this->logger->error('ERROR!'); - return 1; - } + $this->ngCtlRun(['mkpeer', 'o2m:', 'vlan', 'many0', ' downstream']); + $this->ngCtlRun(['name', 'o2m:many0', 'vlan0']); + $this->ngCtlRun(['mkpeer', 'vlan0:', 'eiface', 'vlan0', 'ether']); + + $this->ngCtlRun([ + 'msg', + 'vlan0:', + '\'addfilter { vlan=0 hook="vlan0" }\'' + ]); + $this->ngCtlRun(['msg', 'ngeth0:', 'set', $rgMac]); + $this->logger->info('OK!'); $this->logger->info(" defining etf for $ont (ONT)..."); - if ( - $this->ngCtlRun('mkpeer o2m: etf many1 downstream') - && $this->ngCtlRun('name o2m:many1 waneapfilter') - && $this->ngCtlRun("connect waneapfilter: $ont: nomatch upper") - ) { - $this->logger->info('OK!'); - } else { - $this->logger->error('ERROR!'); - return 1; - } + $this->ngCtlRun(['mkpeer', 'o2m:', 'etf', 'many1', 'downstream']); + $this->ngCtlRun(['name', 'o2m:many1', 'waneapfilter']); + $this->ngCtlRun([ + 'connect', + 'waneapfilter:', + $ont . ':', + 'nomatch', + 'upper' + ]); + $this->logger->info('OK!'); $this->logger->info(" defining etf for $rg (RG)..."); - if ( - $this->ngCtlRun("mkpeer $rg: etf lower downstream") - && $this->ngCtlRun("name $rg:lower laneapfilter") - && $this->ngCtlRun("connect laneapfilter: $rg: nomatch upper") - ) { - $this->logger->info('OK!'); - } else { - $this->logger->error('ERROR!'); - return 1; - } + $this->ngCtlRun(['mkpeer', $rg . ':', 'etf', 'lower', 'downstream']); + $this->ngCtlRun(['name', $rg . ':', 'lower', 'laneapfilter']); + $this->ngCtlRun([ + 'connect', + 'laneapfilter:', + $rg . ':', + 'nomatch', + 'upper' + ]); + $this->logger->info('OK!'); $this->logger->info(" bridging etf for $ont <-> $rg..."); - if ( - $this->ngCtlRun('connect waneapfilter: laneapfilter: eapout eapout') - ) { - $this->logger->info('OK!'); - } else { - $this->logger->error('ERROR!'); - return 1; - } + $this->ngCtlRun([ + 'connect', + 'waneapfilter:', + 'laneapfilter:', + 'eapout', + 'eapout' + ]); + $this->logger->info('OK!'); $this->logger->info(" defining filters for EAP traffic..."); - if ( - $this->ngCtlRun('msg waneapfilter: \'setfilter { matchhook="eapout" ethertype=0x888e }\'') - && $this->ngCtlRun('msg laneapfilter: \'setfilter { matchhook="eapout" ethertype=0x888e }\'') - ) { - $this->logger->info('OK!'); - } else { - $this->logger->error('ERROR!'); - return 1; - } + $this->ngCtlRun([ + 'msg', + 'waneapfilter:', + '\'setfilter { matchhook="eapout" ethertype=0x888e }\'' + ]); + $this->ngCtlRun([ + 'msg', + 'laneapfilter:', + '\'setfilter { matchhook="eapout" ethertype=0x888e }\'' + ]); + $this->logger->info('OK!'); $this->logger->info(" enabling one2many links..."); - if ( - $this->ngCtlRun('msg o2m: setconfig "{ xmitAlg=2 failAlg=1 enabledLinks=[ 1 1 ] }"') - ) { - $this->logger->info('OK!'); - } else { - $this->logger->error('ERROR!'); - return 1; - } + $this->ngCtlRun([ + 'msg', + 'o2m:', + 'setconfig', + '"{ xmitAlg=2 failAlg=1 enabledLinks=[ 1 1 ] }"' + ]); + $this->logger->info('OK!'); $this->logger->info(" removing waneapfilter:nomatch hook..."); - if ( - $this->ngCtlRun('rmhook waneapfilter: nomatch') - ) { - $this->logger->info('OK!'); - } else { - $this->logger->error('ERROR!'); - return 1; - } + $this->ngCtlRun(['rmhook', 'waneapfilter:', 'nomatch']); + $this->logger->info('OK!'); $this->logger->info(" enabling $rg interface..."); - if ( - $this->ngCtlRun("$rg up") - ) { - $this->logger->info('OK!'); - } else { - $this->logger->error('ERROR!'); - return 1; - } + $this->ngCtlRun([$rg, 'up']); + $this->logger->info('OK!'); + $this->logger->info(" enabling $ont interface..."); - if ( - $this->ngCtlRun("$ont up") - ) { - $this->logger->info('OK!'); - } else { - $this->logger->error('ERROR!'); - return 1; - } + $this->ngCtlRun([$ont, 'up"']); + $this->logger->info('OK!'); + $this->logger->info(" enabling promiscuous mode on $rg..."); - if ( - $this->ngCtlRun("$rg promisc") - ) { - $this->logger->info('OK!'); - } else { - $this->logger->error('ERROR!'); - return 1; - } + $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"); - return 0; } /** - * Check filter status. + * Run a ngctl command. * - * @param string $arg + * @param array $args * Additional arguments to pass to ngctl. * * @return bool - * Process return code. + * True on success, false on failure. */ - private function ngCtlRun(string $arg): bool + private function ngCtlRun(array $args): bool { - $process = new Process( - [ - '/usr/sbin/ngctl', - $arg - ], - '/tmp' - ); - return (bool)$process->run(); + $process = new Process(['/usr/sbin/ngctl', ...$args], '/tmp'); + return !$process->run(); } /**