From a0e3ff0e88f5c571a09822b2b9f567a1edb96315 Mon Sep 17 00:00:00 2001 From: TJ da Tuna Date: Tue, 3 Feb 2026 17:33:29 -0600 Subject: [PATCH 1/5] Trust device's reported uplink speed over upstream port table When a UniFi device (AP, modem, etc.) has a wired uplink through an unmanaged switch, the upstream UniFi switch port may report a different speed than what the device actually negotiates. For example, an AP with 1 GbE connected to an unmanaged PoE switch, which is connected to a gateway via 10 GbE SFP+ - the gateway port shows 10 GbE but the AP reports 1 GbE. Now prefer the device's own reported uplink speed (from Uplink.Speed in the API response) over the upstream port table lookup, with fallback to the port table if the device doesn't report a speed. Fixes #189 --- .../NetworkPathAnalyzer.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs b/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs index e3490ea12..67c7d5d0b 100644 --- a/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs +++ b/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs @@ -784,8 +784,13 @@ internal void BuildHopList( } else if (!string.IsNullOrEmpty(currentMac) && currentPort.HasValue) { - // Wired uplink - get port speed from upstream switch - deviceHop.IngressSpeedMbps = GetPortSpeedFromRawDevices(rawDevices, currentMac, currentPort); + // Wired uplink - prefer device's reported uplink speed (from API Uplink.Speed), + // fall back to upstream switch's port table if not available. + // This handles scenarios where there's an unmanaged switch between the device + // and the UniFi switch, where the upstream port may report a different speed. + deviceHop.IngressSpeedMbps = targetDevice.UplinkSpeedMbps > 0 + ? targetDevice.UplinkSpeedMbps + : GetPortSpeedFromRawDevices(rawDevices, currentMac, currentPort); deviceHop.EgressSpeedMbps = deviceHop.IngressSpeedMbps; } @@ -1104,8 +1109,13 @@ internal void BuildHopList( } else { + // Wired uplink - prefer device's reported uplink speed (from API Uplink.Speed), + // fall back to upstream device's port table if not available. + // This handles scenarios where there's an unmanaged switch between devices. hop.EgressPort = device.UplinkPort; - hop.EgressSpeedMbps = GetPortSpeedFromRawDevices(rawDevices, device.UplinkMac, device.UplinkPort); + hop.EgressSpeedMbps = device.UplinkSpeedMbps > 0 + ? device.UplinkSpeedMbps + : GetPortSpeedFromRawDevices(rawDevices, device.UplinkMac, device.UplinkPort); hop.EgressPortName = GetPortName(rawDevices, device.UplinkMac, device.UplinkPort); } } From 757dac5f9ba7e7164fedb9639f399a9d1fb1f5dd Mon Sep 17 00:00:00 2001 From: TJ da Tuna Date: Tue, 3 Feb 2026 17:47:17 -0600 Subject: [PATCH 2/5] Add debug logging for wired uplink speed selection Logs when the device's reported uplink speed differs from the upstream port table, making it clear which value is being used for path tracing. --- .../NetworkPathAnalyzer.cs | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs b/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs index 67c7d5d0b..2c7e14dc4 100644 --- a/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs +++ b/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs @@ -788,10 +788,21 @@ internal void BuildHopList( // fall back to upstream switch's port table if not available. // This handles scenarios where there's an unmanaged switch between the device // and the UniFi switch, where the upstream port may report a different speed. - deviceHop.IngressSpeedMbps = targetDevice.UplinkSpeedMbps > 0 - ? targetDevice.UplinkSpeedMbps - : GetPortSpeedFromRawDevices(rawDevices, currentMac, currentPort); + var portTableSpeed = GetPortSpeedFromRawDevices(rawDevices, currentMac, currentPort); + var useDeviceSpeed = targetDevice.UplinkSpeedMbps > 0; + deviceHop.IngressSpeedMbps = useDeviceSpeed ? targetDevice.UplinkSpeedMbps : portTableSpeed; deviceHop.EgressSpeedMbps = deviceHop.IngressSpeedMbps; + + if (useDeviceSpeed && portTableSpeed > 0 && targetDevice.UplinkSpeedMbps != portTableSpeed) + { + _logger.LogDebug("Wired device {Name}: Using device-reported uplink speed {DeviceSpeed}Mbps (upstream port reports {PortSpeed}Mbps)", + targetDevice.Name, targetDevice.UplinkSpeedMbps, portTableSpeed); + } + else + { + _logger.LogDebug("Wired device {Name}: Uplink speed {Speed}Mbps (device={DeviceSpeed}, port={PortSpeed})", + targetDevice.Name, deviceHop.IngressSpeedMbps, targetDevice.UplinkSpeedMbps, portTableSpeed); + } } hops.Add(deviceHop); @@ -1113,10 +1124,16 @@ internal void BuildHopList( // fall back to upstream device's port table if not available. // This handles scenarios where there's an unmanaged switch between devices. hop.EgressPort = device.UplinkPort; - hop.EgressSpeedMbps = device.UplinkSpeedMbps > 0 - ? device.UplinkSpeedMbps - : GetPortSpeedFromRawDevices(rawDevices, device.UplinkMac, device.UplinkPort); + var portTableSpeed = GetPortSpeedFromRawDevices(rawDevices, device.UplinkMac, device.UplinkPort); + var useDeviceSpeed = device.UplinkSpeedMbps > 0; + hop.EgressSpeedMbps = useDeviceSpeed ? device.UplinkSpeedMbps : portTableSpeed; hop.EgressPortName = GetPortName(rawDevices, device.UplinkMac, device.UplinkPort); + + if (useDeviceSpeed && portTableSpeed > 0 && device.UplinkSpeedMbps != portTableSpeed) + { + _logger.LogDebug("Wired hop {Name}: Using device-reported uplink speed {DeviceSpeed}Mbps (upstream port reports {PortSpeed}Mbps)", + device.Name, device.UplinkSpeedMbps, portTableSpeed); + } } } From 74d7032fc68848ebbe486a852cd3505622ed6dda Mon Sep 17 00:00:00 2001 From: TJ da Tuna Date: Tue, 3 Feb 2026 18:15:13 -0600 Subject: [PATCH 3/5] Add tests for uplink speed preference logic Tests verify that when a device's reported uplink speed differs from the upstream port table (unmanaged switch scenario), the device's speed is preferred. Also tests fallback to port table when device reports 0, and confirms mesh APs continue to use wireless path. --- .../UplinkSpeedPreferenceTests.cs | 291 ++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 tests/NetworkOptimizer.UniFi.Tests/UplinkSpeedPreferenceTests.cs diff --git a/tests/NetworkOptimizer.UniFi.Tests/UplinkSpeedPreferenceTests.cs b/tests/NetworkOptimizer.UniFi.Tests/UplinkSpeedPreferenceTests.cs new file mode 100644 index 000000000..0cd462c0a --- /dev/null +++ b/tests/NetworkOptimizer.UniFi.Tests/UplinkSpeedPreferenceTests.cs @@ -0,0 +1,291 @@ +using FluentAssertions; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Logging; +using Moq; +using NetworkOptimizer.Core.Enums; +using NetworkOptimizer.UniFi.Models; +using NetworkOptimizer.UniFi.Tests.Fixtures; +using Xunit; + +namespace NetworkOptimizer.UniFi.Tests; + +/// +/// Tests for uplink speed preference when device-reported speed differs from port table. +/// Verifies fix for issue #189: When an unmanaged switch sits between a UniFi device and +/// the upstream UniFi switch/gateway, the device's reported uplink speed should be trusted +/// over the upstream port table. +/// +public class UplinkSpeedPreferenceTests +{ + private readonly NetworkPathAnalyzer _analyzer; + private readonly Mock _clientProviderMock; + private readonly IMemoryCache _cache; + private readonly Mock _loggerFactoryMock; + + // Test constants + private const string GatewayMac = "aa:bb:cc:00:00:01"; + private const string ApMac = "aa:bb:cc:00:00:03"; + private const string ServerMac = "aa:bb:cc:00:02:01"; + + public UplinkSpeedPreferenceTests() + { + _clientProviderMock = new Mock(); + _cache = new MemoryCache(new MemoryCacheOptions()); + _loggerFactoryMock = new Mock(); + _loggerFactoryMock.Setup(f => f.CreateLogger(It.IsAny())) + .Returns(new Mock().Object); + + _analyzer = new NetworkPathAnalyzer( + _clientProviderMock.Object, + _cache, + _loggerFactoryMock.Object); + } + + /// + /// When a target device (AP) reports 1 GbE uplink but the upstream gateway port shows 10 GbE + /// (unmanaged switch scenario), the path should use the device's reported 1 GbE speed. + /// + [Fact] + public void BuildHopList_TargetDeviceReportsDifferentSpeed_UsesDeviceSpeed() + { + // Arrange - AP reports 1 GbE, but gateway port 9 shows 10 GbE (unmanaged switch in between) + var gateway = NetworkTestData.CreateGateway(mac: GatewayMac); + var ap = NetworkTestData.CreateWiredAccessPoint( + mac: ApMac, + uplinkMac: GatewayMac, + uplinkPort: 9, + uplinkSpeed: 1000); // AP reports 1 GbE + + var topology = new NetworkTopology + { + Devices = new List { gateway, ap }, + Clients = new List(), + Networks = new List + { + new NetworkInfo { Id = "default", Name = "Default", VlanId = 1, IpSubnet = "192.0.2.0/24" } + } + }; + + var serverPosition = new ServerPosition + { + IpAddress = "192.0.2.200", + Mac = ServerMac, + SwitchMac = GatewayMac, + SwitchPort = 1, + VlanId = 1 + }; + + // Raw devices with port table showing 10 GbE on port 9 (the gateway's SFP+ port) + var rawDevices = new Dictionary + { + [GatewayMac] = new UniFiDeviceResponse + { + Mac = GatewayMac, + PortTable = new List + { + new SwitchPort { PortIdx = 1, Speed = 1000, Up = true }, // Server port + new SwitchPort { PortIdx = 9, Speed = 10000, Up = true } // SFP+ to unmanaged switch + } + } + }; + + var path = new NetworkPath + { + SourceHost = serverPosition.IpAddress, + DestinationHost = ap.IpAddress, + RequiresRouting = false + }; + + // Act + _analyzer.BuildHopList(path, serverPosition, ap, null, topology, rawDevices); + + // Assert - AP hop should use device-reported 1 GbE, not port table's 10 GbE + var apHop = path.Hops.FirstOrDefault(h => h.DeviceMac == ApMac); + apHop.Should().NotBeNull("AP should be in the path"); + apHop!.IngressSpeedMbps.Should().Be(1000, + "should use AP's reported uplink speed (1 GbE), not upstream port table (10 GbE)"); + apHop.EgressSpeedMbps.Should().Be(1000, + "egress should match ingress for symmetric link"); + } + + /// + /// When a device reports 0 for uplink speed (not available), should fall back to port table. + /// + [Fact] + public void BuildHopList_DeviceReportsZeroSpeed_FallsBackToPortTable() + { + // Arrange - AP reports 0 (unknown), port table shows 2500 + var gateway = NetworkTestData.CreateGateway(mac: GatewayMac); + var ap = NetworkTestData.CreateWiredAccessPoint( + mac: ApMac, + uplinkMac: GatewayMac, + uplinkPort: 9, + uplinkSpeed: 0); // AP doesn't report speed + + var topology = new NetworkTopology + { + Devices = new List { gateway, ap }, + Clients = new List(), + Networks = new List + { + new NetworkInfo { Id = "default", Name = "Default", VlanId = 1, IpSubnet = "192.0.2.0/24" } + } + }; + + var serverPosition = new ServerPosition + { + IpAddress = "192.0.2.200", + Mac = ServerMac, + SwitchMac = GatewayMac, + SwitchPort = 1, + VlanId = 1 + }; + + var rawDevices = new Dictionary + { + [GatewayMac] = new UniFiDeviceResponse + { + Mac = GatewayMac, + PortTable = new List + { + new SwitchPort { PortIdx = 1, Speed = 1000, Up = true }, + new SwitchPort { PortIdx = 9, Speed = 2500, Up = true } + } + } + }; + + var path = new NetworkPath + { + SourceHost = serverPosition.IpAddress, + DestinationHost = ap.IpAddress, + RequiresRouting = false + }; + + // Act + _analyzer.BuildHopList(path, serverPosition, ap, null, topology, rawDevices); + + // Assert - Should fall back to port table when device reports 0 + var apHop = path.Hops.FirstOrDefault(h => h.DeviceMac == ApMac); + apHop.Should().NotBeNull(); + apHop!.IngressSpeedMbps.Should().Be(2500, + "should fall back to port table (2500) when device reports 0"); + } + + /// + /// When device and port table report the same speed, should use that speed (no override needed). + /// + [Fact] + public void BuildHopList_SpeedsMatch_UsesThatSpeed() + { + // Arrange - Both report 2500 + var gateway = NetworkTestData.CreateGateway(mac: GatewayMac); + var ap = NetworkTestData.CreateWiredAccessPoint( + mac: ApMac, + uplinkMac: GatewayMac, + uplinkPort: 9, + uplinkSpeed: 2500); + + var topology = new NetworkTopology + { + Devices = new List { gateway, ap }, + Clients = new List(), + Networks = new List + { + new NetworkInfo { Id = "default", Name = "Default", VlanId = 1, IpSubnet = "192.0.2.0/24" } + } + }; + + var serverPosition = new ServerPosition + { + IpAddress = "192.0.2.200", + Mac = ServerMac, + SwitchMac = GatewayMac, + SwitchPort = 1, + VlanId = 1 + }; + + var rawDevices = new Dictionary + { + [GatewayMac] = new UniFiDeviceResponse + { + Mac = GatewayMac, + PortTable = new List + { + new SwitchPort { PortIdx = 1, Speed = 1000, Up = true }, + new SwitchPort { PortIdx = 9, Speed = 2500, Up = true } + } + } + }; + + var path = new NetworkPath + { + SourceHost = serverPosition.IpAddress, + DestinationHost = ap.IpAddress, + RequiresRouting = false + }; + + // Act + _analyzer.BuildHopList(path, serverPosition, ap, null, topology, rawDevices); + + // Assert + var apHop = path.Hops.FirstOrDefault(h => h.DeviceMac == ApMac); + apHop.Should().NotBeNull(); + apHop!.IngressSpeedMbps.Should().Be(2500); + } + + /// + /// Mesh APs should continue to use wireless uplink path (not affected by this fix). + /// + [Fact] + public void BuildHopList_MeshAp_UsesWirelessPath() + { + // Arrange + var gateway = NetworkTestData.CreateGateway(mac: GatewayMac); + var wiredAp = NetworkTestData.CreateWiredAccessPoint( + mac: "aa:bb:cc:00:00:02", + uplinkMac: GatewayMac, + uplinkPort: 1, + uplinkSpeed: 1000); + var meshAp = NetworkTestData.CreateMeshAccessPoint( + mac: ApMac, + uplinkMac: "aa:bb:cc:00:00:02", + txRateKbps: 866000, + rxRateKbps: 866000); + + var topology = new NetworkTopology + { + Devices = new List { gateway, wiredAp, meshAp }, + Clients = new List(), + Networks = new List + { + new NetworkInfo { Id = "default", Name = "Default", VlanId = 1, IpSubnet = "192.0.2.0/24" } + } + }; + + var serverPosition = new ServerPosition + { + IpAddress = "192.0.2.200", + Mac = ServerMac, + SwitchMac = GatewayMac, + SwitchPort = 2, + VlanId = 1 + }; + + var path = new NetworkPath + { + SourceHost = serverPosition.IpAddress, + DestinationHost = meshAp.IpAddress, + RequiresRouting = false + }; + + // Act + _analyzer.BuildHopList(path, serverPosition, meshAp, null, topology, new Dictionary()); + + // Assert - Mesh AP should have wireless flags set + var meshHop = path.Hops.FirstOrDefault(h => h.DeviceMac == ApMac); + meshHop.Should().NotBeNull(); + meshHop!.IsWirelessIngress.Should().BeTrue("mesh AP uplink should be marked as wireless"); + meshHop.IsWirelessEgress.Should().BeTrue("mesh AP uplink should be marked as wireless"); + meshHop.IngressSpeedMbps.Should().Be(866, "should use mesh uplink speed (866 Mbps from 866000 Kbps)"); + } +} From 619b0085a2b1ad651a1e88be2a7b6a2df108b0ed Mon Sep 17 00:00:00 2001 From: TJ da Tuna Date: Tue, 3 Feb 2026 18:20:28 -0600 Subject: [PATCH 4/5] Exclude gateways from uplink speed preference Gateway UplinkSpeedMbps represents WAN speed, not LAN port speed. Only apply the device-reported uplink speed preference for non-gateway devices (APs, switches, modems, etc.). Adds test to verify gateways use port table speed. --- .../NetworkPathAnalyzer.cs | 6 +- .../UplinkSpeedPreferenceTests.cs | 78 +++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs b/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs index 2c7e14dc4..8a71971ac 100644 --- a/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs +++ b/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs @@ -788,8 +788,9 @@ internal void BuildHopList( // fall back to upstream switch's port table if not available. // This handles scenarios where there's an unmanaged switch between the device // and the UniFi switch, where the upstream port may report a different speed. + // NOTE: Skip for gateways - their UplinkSpeedMbps is WAN speed, not LAN port speed. var portTableSpeed = GetPortSpeedFromRawDevices(rawDevices, currentMac, currentPort); - var useDeviceSpeed = targetDevice.UplinkSpeedMbps > 0; + var useDeviceSpeed = targetDevice.UplinkSpeedMbps > 0 && targetDevice.Type != DeviceType.Gateway; deviceHop.IngressSpeedMbps = useDeviceSpeed ? targetDevice.UplinkSpeedMbps : portTableSpeed; deviceHop.EgressSpeedMbps = deviceHop.IngressSpeedMbps; @@ -1123,9 +1124,10 @@ internal void BuildHopList( // Wired uplink - prefer device's reported uplink speed (from API Uplink.Speed), // fall back to upstream device's port table if not available. // This handles scenarios where there's an unmanaged switch between devices. + // NOTE: Skip for gateways - their UplinkSpeedMbps is WAN speed, not LAN port speed. hop.EgressPort = device.UplinkPort; var portTableSpeed = GetPortSpeedFromRawDevices(rawDevices, device.UplinkMac, device.UplinkPort); - var useDeviceSpeed = device.UplinkSpeedMbps > 0; + var useDeviceSpeed = device.UplinkSpeedMbps > 0 && device.Type != DeviceType.Gateway; hop.EgressSpeedMbps = useDeviceSpeed ? device.UplinkSpeedMbps : portTableSpeed; hop.EgressPortName = GetPortName(rawDevices, device.UplinkMac, device.UplinkPort); diff --git a/tests/NetworkOptimizer.UniFi.Tests/UplinkSpeedPreferenceTests.cs b/tests/NetworkOptimizer.UniFi.Tests/UplinkSpeedPreferenceTests.cs index 0cd462c0a..9d2bcec23 100644 --- a/tests/NetworkOptimizer.UniFi.Tests/UplinkSpeedPreferenceTests.cs +++ b/tests/NetworkOptimizer.UniFi.Tests/UplinkSpeedPreferenceTests.cs @@ -288,4 +288,82 @@ public void BuildHopList_MeshAp_UsesWirelessPath() meshHop.IsWirelessEgress.Should().BeTrue("mesh AP uplink should be marked as wireless"); meshHop.IngressSpeedMbps.Should().Be(866, "should use mesh uplink speed (866 Mbps from 866000 Kbps)"); } + + /// + /// Gateways should NOT use UplinkSpeedMbps (that's WAN speed) - should use port table instead. + /// + [Fact] + public void BuildHopList_GatewayTarget_UsesPortTableNotWanSpeed() + { + // Arrange - Gateway has 1000 Mbps WAN, but LAN port is 2500 Mbps + var gateway = new DiscoveredDevice + { + Mac = GatewayMac, + IpAddress = "192.0.2.1", + Name = "Gateway", + Model = "UDM-Pro", + Type = DeviceType.Gateway, + Adopted = true, + State = 1, + UplinkSpeedMbps = 1000, // This is WAN speed - should NOT be used for LAN path + IsUplinkConnected = true + }; + + var ap = NetworkTestData.CreateWiredAccessPoint( + mac: ApMac, + uplinkMac: GatewayMac, + uplinkPort: 9, + uplinkSpeed: 2500); + + var topology = new NetworkTopology + { + Devices = new List { gateway, ap }, + Clients = new List(), + Networks = new List + { + new NetworkInfo { Id = "default", Name = "Default", VlanId = 1, IpSubnet = "192.0.2.0/24" } + } + }; + + var serverPosition = new ServerPosition + { + IpAddress = "192.0.2.200", + Mac = ServerMac, + SwitchMac = GatewayMac, + SwitchPort = 1, + VlanId = 1 + }; + + // Raw devices with port table - LAN ports are 2500 Mbps + var rawDevices = new Dictionary + { + [GatewayMac] = new UniFiDeviceResponse + { + Mac = GatewayMac, + PortTable = new List + { + new SwitchPort { PortIdx = 1, Speed = 2500, Up = true }, // Server port + new SwitchPort { PortIdx = 9, Speed = 2500, Up = true } // AP port + } + } + }; + + var path = new NetworkPath + { + SourceHost = serverPosition.IpAddress, + DestinationHost = gateway.IpAddress, + RequiresRouting = false, + TargetIsGateway = true + }; + + // Act + _analyzer.BuildHopList(path, serverPosition, gateway, null, topology, rawDevices); + + // Assert - Gateway hop should use port table (2500), NOT UplinkSpeedMbps (1000 WAN) + var gatewayHop = path.Hops.FirstOrDefault(h => h.DeviceMac == GatewayMac); + gatewayHop.Should().NotBeNull("gateway should be in the path"); + // Gateway's ingress/egress should NOT be 1000 (WAN speed) + gatewayHop!.IngressSpeedMbps.Should().NotBe(1000, + "should NOT use gateway's WAN uplink speed for LAN path"); + } } From 4a53792c24647119f9f368359c28240ba8872751 Mon Sep 17 00:00:00 2001 From: TJ da Tuna Date: Tue, 3 Feb 2026 18:33:08 -0600 Subject: [PATCH 5/5] Restore uplink speed preference for intermediate hops toward gateway MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fix should apply when going TOWARD gateway (uplink direction matches): - Target device: uses device uplink speed ✓ - Intermediate hops in main loop: uses device uplink speed ✓ Return path (AWAY from gateway) correctly uses port table: - Daisy-chain downstream: port table ✓ - Inter-VLAN return path: port table (except first hop ingress) ✓ --- src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs b/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs index 8a71971ac..196e99675 100644 --- a/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs +++ b/src/NetworkOptimizer.UniFi/NetworkPathAnalyzer.cs @@ -1124,6 +1124,8 @@ internal void BuildHopList( // Wired uplink - prefer device's reported uplink speed (from API Uplink.Speed), // fall back to upstream device's port table if not available. // This handles scenarios where there's an unmanaged switch between devices. + // NOTE: Only applies when going TOWARD gateway (main loop). Return path + // (after gateway) uses port table since we're going opposite of uplink direction. // NOTE: Skip for gateways - their UplinkSpeedMbps is WAN speed, not LAN port speed. hop.EgressPort = device.UplinkPort; var portTableSpeed = GetPortSpeedFromRawDevices(rawDevices, device.UplinkMac, device.UplinkPort);