From 0e9ff81cf5e54b3da415dce1000df702f232bba9 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Fri, 4 Nov 2022 09:03:26 +0000 Subject: [PATCH] Add worker to collect various PortAccessEntity (NAC) attributes (PR #937, partially implements #887) * Add macsuck worker to collect various PortAccessEntity (NAC) attributes * Incorporate PAE feedback on #937 * missing Result/Device.pm column added * pae_is... columns instead of pae_capabilities * moved most code to Util/PortAccessEntity.pm so the update can be done in discover and macsuck * Refactor PAE attributes during discover as separate Plugin * PortAccessEntity: don't use device->dns in log string * Fix "Experimental keys on scalar is now forbidden" test failure * Revamp pae_control and add missing attribute - device.pae_control (text) is now device.pae_is_enabled (bool) - also store pae_authconfig_port_control (port mode auto/force(un)Auth) * Fix "Experimental keys on scalar is now forbidden" test failure - ... again because of botched merge - at least perlgolfed away a set of curly braces * Update PortAccessEntity.pm * Incorporate @ollyg PR feedback Co-authored-by: Christian Ramseyer --- lib/App/Netdisco/DB/Result/Device.pm | 2 + .../DB/Result/DevicePortProperties.pm | 19 ++++- lib/App/Netdisco/Util/PortAccessEntity.pm | 85 +++++++++++++++++++ .../PortProperties/PortAccessEntity.pm | 18 ++++ .../Plugin/Macsuck/Nodes/PortAccessEntity.pm | 18 ++++ share/config.yml | 2 + .../App-Netdisco-DB-75-76-PostgreSQL.sql | 14 +++ 7 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 lib/App/Netdisco/Util/PortAccessEntity.pm create mode 100644 lib/App/Netdisco/Worker/Plugin/Discover/PortProperties/PortAccessEntity.pm create mode 100644 lib/App/Netdisco/Worker/Plugin/Macsuck/Nodes/PortAccessEntity.pm create mode 100644 share/schema_versions/App-Netdisco-DB-75-76-PostgreSQL.sql diff --git a/lib/App/Netdisco/DB/Result/Device.pm b/lib/App/Netdisco/DB/Result/Device.pm index f240c177..5eeb8776 100644 --- a/lib/App/Netdisco/DB/Result/Device.pm +++ b/lib/App/Netdisco/DB/Result/Device.pm @@ -83,6 +83,8 @@ __PACKAGE__->add_columns( { data_type => "timestamp", is_nullable => 1 }, "is_pseudo", { data_type => "boolean", is_nullable => 0, default_value => \"false" }, + "pae_is_enabled", + { data_type => "boolean", is_nullable => 1 }, ); __PACKAGE__->set_primary_key("ip"); diff --git a/lib/App/Netdisco/DB/Result/DevicePortProperties.pm b/lib/App/Netdisco/DB/Result/DevicePortProperties.pm index ece7174b..c69ceed1 100644 --- a/lib/App/Netdisco/DB/Result/DevicePortProperties.pm +++ b/lib/App/Netdisco/DB/Result/DevicePortProperties.pm @@ -25,7 +25,7 @@ __PACKAGE__->add_columns( { data_type => "text", is_nullable => 1 }, "remote_os_ver", { data_type => "text", is_nullable => 1 }, - "remote_serial", + "remote_serial", { data_type => "text", is_nullable => 1 }, "remote_dns", { data_type => "text", is_nullable => 1 }, @@ -35,6 +35,23 @@ __PACKAGE__->add_columns( { data_type => "boolean", default_value => \"false", is_nullable => 1 }, "ifindex", { data_type => "bigint", is_nullable => 1 }, + "pae_authconfig_state", + { data_type => "text", is_nullable => 1 }, + "pae_authconfig_port_control", + { data_type => "text", is_nullable => 1 }, + "pae_authconfig_port_status", + { data_type => "text", is_nullable => 1 }, + "pae_authsess_user", + { data_type => "text", is_nullable => 1 }, + "pae_authsess_mab", + { data_type => "text", is_nullable => 1 }, + "pae_last_eapol_frame_source", + { data_type => "text", is_nullable => 1 }, + "pae_is_authenticator", + { data_type => "boolean", default_value => \"false", is_nullable => 1 }, + "pae_is_supplicant", + { data_type => "boolean", default_value => \"false", is_nullable => 1 }, + ); __PACKAGE__->set_primary_key("port", "ip"); diff --git a/lib/App/Netdisco/Util/PortAccessEntity.pm b/lib/App/Netdisco/Util/PortAccessEntity.pm new file mode 100644 index 00000000..185d9806 --- /dev/null +++ b/lib/App/Netdisco/Util/PortAccessEntity.pm @@ -0,0 +1,85 @@ +package App::Netdisco::Util::PortAccessEntity; + +use Dancer qw/:syntax/; +use Dancer::Plugin::DBIC 'schema'; +use aliased 'App::Netdisco::Worker::Status'; + + +use base 'Exporter'; +our @EXPORT = (); +our @EXPORT_OK = qw/update_pae_attributes/; +our %EXPORT_TAGS = (all => \@EXPORT_OK); + +=head1 NAME + +App::Netdisco::Util::PortAccessEntity + +=head1 DESCRIPTION + +Helper subroutines to update PAE details in device_port_properties +These are updated both during discover and macsuck. + +=cut + +sub update_pae_attributes { + my ($device) = @_; + no warnings "uninitialized"; + + my $snmp = App::Netdisco::Transport::SNMP->reader_for($device) + or return Status->defer("pae failed: could not SNMP connect to $device"); + debug sprintf ' [%s] pae - updating PortAccessEntity details', $device->ip; + + # device property + my $pae_control = $snmp->pae_control(); + + if ($pae_control and $pae_control eq 'enabled') { + debug sprintf ' [%s] pae - PortAccessEntity device-wide support: %s', $device->ip, $pae_control; + schema('netdisco')->resultset('Device')->search({ 'me.ip' => $device->ip}) + ->update({ pae_is_enabled => 't' }); + } else { + debug sprintf ' [%s] pae - no PortAccessEntity support, leaving worker', $device->ip; + schema('netdisco')->resultset('Device')->search({ 'me.ip' => $device->ip}) + ->update({ pae_is_enabled => 'f' }); + return Status->info("Skipped pae for $device"); + } + + # individual port properties + my $interfaces = $snmp->interfaces; + my $pae_authconfig_state = $snmp->pae_authconfig_state(); + my $pae_authconfig_port_control = $snmp->dot1xAuthAuthControlledPortControl(); + my $pae_authconfig_port_status = $snmp->pae_authconfig_port_status(); + my $pae_authsess_user = $snmp->pae_authsess_user(); + my $pae_authsess_mab = $snmp->pae_authsess_mab(); + my $pae_capabilities = $snmp->pae_i_capabilities(); + my $pae_last_eapol_frame_source = $snmp->pae_i_last_eapol_frame_source(); + + foreach my $ind (sort keys %$interfaces){ + debug sprintf ' [%s] pae - attributes found for ifindex %s: %s %s %s %s %s %s %s', + $device->ip, $ind, + $pae_authconfig_state->{$ind} || 'no pae_authconfig_state', + $pae_authconfig_port_control->{$ind}, + $pae_authconfig_port_status->{$ind}, + $pae_authsess_user->{$ind}, + $pae_authsess_mab->{$ind}, + $pae_capabilities->{$ind}, + $pae_last_eapol_frame_source->{$ind}; + + schema('netdisco')->resultset('DevicePortProperties') + ->search({ 'me.ip' => $device->ip, 'me.port' => $interfaces->{$ind} }) + ->update({ + pae_authconfig_state => $pae_authconfig_state->{$ind} , + pae_authconfig_port_control => $pae_authconfig_port_control->{$ind} , + pae_authconfig_port_status => $pae_authconfig_port_status->{$ind} , + pae_authsess_user => $pae_authsess_user->{$ind} , + pae_authsess_mab => $pae_authsess_mab->{$ind} , + pae_is_authenticator => $pae_capabilities->{$ind} =~ m/dot1xPaePortAuthCapable/ ? "t" : "f", + pae_is_supplicant => $pae_capabilities->{$ind} =~ m/dot1xPaePortSuppCapable/ ? "t" : "f", + pae_last_eapol_frame_source => $pae_last_eapol_frame_source->{$ind} , + + }); + } + + return Status->info("Completed pae for $device"); +} + +1; diff --git a/lib/App/Netdisco/Worker/Plugin/Discover/PortProperties/PortAccessEntity.pm b/lib/App/Netdisco/Worker/Plugin/Discover/PortProperties/PortAccessEntity.pm new file mode 100644 index 00000000..16b6d48a --- /dev/null +++ b/lib/App/Netdisco/Worker/Plugin/Discover/PortProperties/PortAccessEntity.pm @@ -0,0 +1,18 @@ +package App::Netdisco::Worker::Plugin::Discover::PortProperties::PortAccessEntity; + +use Dancer ':syntax'; +use App::Netdisco::Worker::Plugin; +use aliased 'App::Netdisco::Worker::Status'; +use Dancer::Plugin::DBIC 'schema'; +use App::Netdisco::Util::Worker; +use App::Netdisco::Util::PortAccessEntity qw/update_pae_attributes/; + +register_worker({ phase => 'main', driver => 'snmp' }, sub { + + my ($job, $workerconf) = @_; + my $device = $job->device; + return update_pae_attributes($device) + +}); + +true; diff --git a/lib/App/Netdisco/Worker/Plugin/Macsuck/Nodes/PortAccessEntity.pm b/lib/App/Netdisco/Worker/Plugin/Macsuck/Nodes/PortAccessEntity.pm new file mode 100644 index 00000000..d41b3d7e --- /dev/null +++ b/lib/App/Netdisco/Worker/Plugin/Macsuck/Nodes/PortAccessEntity.pm @@ -0,0 +1,18 @@ +package App::Netdisco::Worker::Plugin::Macsuck::Nodes::PortAccessEntity; + +use Dancer ':syntax'; +use App::Netdisco::Worker::Plugin; +use aliased 'App::Netdisco::Worker::Status'; +use Dancer::Plugin::DBIC 'schema'; +use App::Netdisco::Util::Worker; +use App::Netdisco::Util::PortAccessEntity qw/update_pae_attributes/; + +register_worker({ phase => 'main', driver => 'snmp' }, sub { + + my ($job, $workerconf) = @_; + my $device = $job->device; + return update_pae_attributes($device) + +}); + +true; diff --git a/share/config.yml b/share/config.yml index c7faa5be..fd62cccb 100644 --- a/share/config.yml +++ b/share/config.yml @@ -443,6 +443,7 @@ worker_plugins: - 'Discover::Neighbors::Routed' - 'Discover::PortPower' - 'Discover::PortProperties' + - 'Discover::PortProperties::PortAccessEntity' - 'Discover::Properties' - 'Discover::VLANs' - 'Discover::Wireless' @@ -462,6 +463,7 @@ worker_plugins: - 'Macsuck::Hooks' - 'Macsuck::Nodes' - 'Macsuck::WirelessNodes' + - 'Macsuck::Nodes::PortAccessEntity' - 'Macwalk' - 'MakeRancidConf' - 'Nbtstat' diff --git a/share/schema_versions/App-Netdisco-DB-75-76-PostgreSQL.sql b/share/schema_versions/App-Netdisco-DB-75-76-PostgreSQL.sql new file mode 100644 index 00000000..1c603bbd --- /dev/null +++ b/share/schema_versions/App-Netdisco-DB-75-76-PostgreSQL.sql @@ -0,0 +1,14 @@ +BEGIN; + +ALTER TABLE device ADD COLUMN "pae_is_enabled" boolean; + +ALTER TABLE device_port_properties ADD COLUMN "pae_authconfig_state" text; +ALTER TABLE device_port_properties ADD COLUMN "pae_authconfig_port_control" text; +ALTER TABLE device_port_properties ADD COLUMN "pae_authconfig_port_status" text; +ALTER TABLE device_port_properties ADD COLUMN "pae_authsess_user" text; +ALTER TABLE device_port_properties ADD COLUMN "pae_authsess_mab" text; +ALTER TABLE device_port_properties ADD COLUMN "pae_last_eapol_frame_source" text; +ALTER TABLE device_port_properties ADD COLUMN "pae_is_authenticator" boolean; +ALTER TABLE device_port_properties ADD COLUMN "pae_is_supplicant" boolean; + +COMMIT;