diff --git a/CHANGELOG.md b/CHANGELOG.md index 149fe00..9f4559a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- **Per-function module split.** `NetDiagram-PS.psm1` is a loader that + dot-sources `Private/` then `Public/` (one function per file). See + [STRUCTURE.md](STRUCTURE.md). +- **`Invoke-NetworkDiscovery`.** Enumerate an IPv4 CIDR (Quick/Medium/Full, + /22 cap) and return a topology after `Install-Module` without a repo clone. +- **`Import-NmapScan`.** Ingest nmap `-oX` XML; up hosts become nodes. - **`Import-NmapScan` imports IPv6 hosts.** nmap `-oX` `address` elements with `addrtype='ipv6'` are accepted when a host has no IPv4 address. Dual-stack hosts keep a single node identity: IPv4 is preferred when both families are present. Addresses are parsed with `[System.Net.IPAddress]::Parse` and stored in canonical form; malformed addresses are skipped (same as invalid IPv4). +- **`Export-Mermaid`.** Flowchart LR with subnet subgraphs; L2 solid, + L3-Inferred dotted. +- **`Export-NetBox`.** Devices CSV for NetBox bulk import (`-Site`, + role/status mapping). +- **`Export-NodeInventoryCsv`.** Flat node inventory CSV. +- **`Export-Topology` / `Import-Topology`.** JSON round-trip of Nodes, + Edges, and Subnets. +- **`Get-SnmpBridgeNeighbors`.** Walks BRIDGE-MIB `dot1dTpFdbTable` and + emits `L2-FDB` edges (ranked between L2-SNMP and L3-Inferred) for MACs + that resolve via the local ARP table. +- **IPv6 in subnet matching, ARP, and DrawIO.** `Test-IPInSubnet`, + `Get-LocalARPTable` / `ConvertFrom-ArpText`, and subnet parenting accept + IPv6. Mixed-family placement is a non-match (no throw). +- **`Get-MACVendor -OuiDatabasePath`.** Optional IEEE `oui.csv` for full + OUI coverage (built-in table remains a 31-prefix sample). ### Changed - **`Import-Inventory` accepts dual-stack addresses.** Device `ip` and subnet @@ -21,6 +42,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 string. Each device still has a single identity address; prefer IPv4 when a dual-stack host needs only one row. Discovery (`Invoke-NetworkDiscovery`) remains IPv4-only. +- **Reachability is tri-state.** `Test-DeviceReachability` records `$true` / + `$false` / `$null` (indeterminate, e.g. local send failure). Optional + `-TcpFallbackPort` can promote ICMP-silent hosts. DrawIO paints unknown gray. +- **Packaged CI smoke** imports every `FunctionsToExport` entry, uses + `actions/checkout@v7`, and asserts offline dual-stack DrawIO parenting. ### Fixed - **`Export-DrawIO` L2-FDB edge style.** Bridge forwarding-table edges (`Confidence='L2-FDB'`) @@ -31,6 +57,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 local send failure stays `$null` (Unknown) rather than being painted Unreachable. Optional `-TcpFallbackPort` is forwarded to the same helper. The step-[4/5] summary reports reachable, unreachable, and unknown counts. +- **`Compare-NetworkScans` respects incomplete SNMP.** Nodes skipped because + the current snapshot’s SNMP outcome is `noCredential` or `error` are not + reported as Removed. +- **SNMP per-node outcomes.** `Get-SnmpNeighbors` records + answered/noCredential/error/noData per queried node; `Export-Metadata` + persists `snmpSummary` / `snmpOutcomes`. +- **`Export-DrawIO` / `Export-Metadata` require `-Force` to overwrite.** ## [1.3.0] - 2026-07-30 @@ -198,4 +231,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [1.0.0]: https://github.com/jacobyoby/PSNetMap/releases/tag/v1.0.0 [1.1.0]: https://github.com/jacobyoby/PSNetMap/compare/v1.0.0...v1.1.0 [1.2.0]: https://github.com/jacobyoby/PSNetMap/compare/v1.1.0...v1.2.0 +[unreleased]: https://github.com/jacobyoby/PSNetMap/compare/v1.3.0...HEAD [1.3.0]: https://github.com/jacobyoby/PSNetMap/compare/v1.2.0...v1.3.0 diff --git a/NetDiagram-PS/NetDiagram-PS.psd1 b/NetDiagram-PS/NetDiagram-PS.psd1 index 34bd8db..a017062 100644 --- a/NetDiagram-PS/NetDiagram-PS.psd1 +++ b/NetDiagram-PS/NetDiagram-PS.psd1 @@ -46,10 +46,10 @@ PrivateData = @{ PSData = @{ - Tags = @('Network', 'Discovery', 'Diagram', 'SNMP', 'DrawIO', 'Topology', 'NetworkMapping', 'Cisco', 'LLDP', 'Visualization') + Tags = @('Network', 'Discovery', 'Diagram', 'SNMP', 'DrawIO', 'Topology', 'NetworkMapping', 'Cisco', 'LLDP', 'IPv6', 'Visualization') LicenseUri = 'https://github.com/jacobyoby/PSNetMap/blob/main/LICENSE' ProjectUri = 'https://github.com/jacobyoby/PSNetMap' - ReleaseNotes = 'Version 1.3.0 - Code-review hardening. Get-SnmpNeighbors now queries untested nodes by default (Reachable = $null), fixing the import-then-discover workflow, with an -OnlyReachable opt-in switch. Invoke-SnmpWalk redacts the community string from verbose output, validates all parameters (SNMP version set, IP/FQDN, OID format, argument-injection guard), restricts binary resolution to Application, and documents process-list exposure. LLDP/CDP parsing hardened (value-only, octet-validated, Hex-STRING aware) and clearly documented as best-effort. Export-DrawIO de-duplicates node IPs and parents nodes into their matching subnet containers. Invoke-PortScan validates port/timeout ranges and drops TCP 161 (SNMP is UDP). Resolve-IPHostname now honors -TimeoutSeconds via async DNS. Example wizard uses real CIDR host enumeration (with a /22 scan cap) instead of assuming /24.' + ReleaseNotes = 'Unreleased (post-1.3.0). Module split into Public/Private loader files. IPv6 subnet matching, ARP discovery, DrawIO parenting, and dual-stack Import-Inventory. Import-NmapScan accepts IPv6-only nmap hosts (IPv4 preferred on dual-stack). Get-SnmpBridgeNeighbors walks BRIDGE-MIB forwarding tables and emits L2-FDB edges. New commands: Invoke-NetworkDiscovery, Import-NmapScan, Export-Mermaid, Export-NetBox, Export-NodeInventoryCsv, Export-Topology, Import-Topology. Test-DeviceReachability is tri-state ($true/$false/$null) with optional TCP fallback; the wizard uses the same helper. Packaged CI smoke checks FunctionsToExport and offline IPv6 DrawIO parenting.' } } } diff --git a/README.md b/README.md index 4c68b3f..0967523 100644 --- a/README.md +++ b/README.md @@ -122,13 +122,15 @@ Set-Secret -Name 'MySNMPCommunity' -Secret 'public' # 3. Discover topology with SNMP $topo = Import-Inventory -Path '.\my-inventory.json' $topo = $topo | Get-SnmpNeighbors -CredentialMapPath '.\my-credmap.json' +$topo = $topo | Get-SnmpBridgeNeighbors -CredentialMapPath '.\my-credmap.json' $topo | Export-DrawIO -OutFile '.\network.drawio' ``` -> **Note:** `Get-SnmpNeighbors` queries every imported node by default (nodes whose -> reachability has not been tested are still queried), so the workflow above runs end to -> end without a separate reachability pass. Add `-OnlyReachable` (after piping through -> `Test-DeviceReachability`) to query only nodes that answered a ping. +> **Note:** `Get-SnmpNeighbors` and `Get-SnmpBridgeNeighbors` query every imported node +> by default (nodes whose reachability has not been tested are still queried), so the +> workflow above runs end to end without a separate reachability pass. Add +> `-OnlyReachable` (after piping through `Test-DeviceReachability`) to query only nodes +> that answered a ping. Bridge FDB links are labeled `L2-FDB` and drawn as solid teal. > > **LLDP/CDP parsing is best-effort (MVP):** it extracts typed management-address values > from `snmpwalk` output and links them to known nodes, but does not fully decode the @@ -247,7 +249,9 @@ PSNetMap/ ├── README.md # Main documentation and conventions ├── NetDiagram-PS/ │ ├── NetDiagram-PS.psd1 # Module manifest -│ └── NetDiagram-PS.psm1 # Module implementation +│ ├── NetDiagram-PS.psm1 # Loader (dot-sources Private/ then Public/) +│ ├── Private/ # Internal helpers (not exported) +│ └── Public/ # Exported commands (one file per function) ├── examples/ │ ├── New-NetworkDiagram.ps1 # Guided discovery wizard │ ├── credmap.json # SNMP credential-map template @@ -256,8 +260,9 @@ PSNetMap/ └── NetDiagram.Tests.ps1 # Pester test suite ``` -This is the single maintained repository-layout reference. Update it when tracked -top-level files or test entry points change. +This tree is the top-level layout. Per-file module contents and loader +conventions live in [STRUCTURE.md](STRUCTURE.md). Update both when tracked +top-level files, Public/Private files, or test entry points change. ### Keep generated network data outside the clone @@ -298,7 +303,8 @@ After importing the module, you have access to: | `Resolve-IPHostname` | Perform reverse DNS lookups | | `Get-MACVendor` | Look up vendors for MAC addresses | | `Invoke-PortScan` | Check TCP ports with optional banner grabbing | -| `Get-SnmpNeighbors` | Discover connections via SNMP | +| `Get-SnmpNeighbors` | Discover connections via SNMP (LLDP/CDP) | +| `Get-SnmpBridgeNeighbors` | Discover L2-FDB edges from switch forwarding tables | | `Get-CommonSNMPStrings` | Suggest likely SNMP community strings | | `Merge-Edges` | Combine and deduplicate connections | | `Export-DrawIO` | Generate diagram file | diff --git a/STRUCTURE.md b/STRUCTURE.md index f416f21..0df80bf 100644 --- a/STRUCTURE.md +++ b/STRUCTURE.md @@ -53,6 +53,7 @@ NetDiagram-PS/ ├── Get-CommonSNMPStrings.ps1 ├── Get-LocalARPTable.ps1 ├── Get-MACVendor.ps1 + ├── Get-SnmpBridgeNeighbors.ps1 ├── Get-SnmpNeighbors.ps1 ├── Import-Inventory.ps1 ├── Import-NmapScan.ps1