Skip to content
Merged
298 changes: 223 additions & 75 deletions Source/NETworkManager.Models/Network/NetworkInterface.cs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Source/NETworkManager.Profiles/ProfileManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ public static void Save()

return;
}


Directory.CreateDirectory(GetProfilesFolderLocation());

Expand Down
44 changes: 29 additions & 15 deletions Source/NETworkManager/ViewModels/IPScannerViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ private void ScanAction()

private void DetectSubnetAction()
{
DetectIPRange().ConfigureAwait(false);
DetectSubnet().ConfigureAwait(false);
}

/// <summary>
Expand Down Expand Up @@ -530,34 +530,47 @@ private void Stop()
}

/// <summary>
/// Detects the local IP subnet.
/// Attempts to detect the local subnet and updates the host information accordingly.
/// </summary>
private async Task DetectIPRange()
/// <remarks>If the subnet or local IP address cannot be detected, an error message is displayed to the
/// user. The method updates the Host property with the detected subnet in CIDR notation when successful.</remarks>
/// <returns>A task that represents the asynchronous subnet detection operation.</returns>
private async Task DetectSubnet()
{
IsSubnetDetectionRunning = true;

// Try to detect local IP address based on routing to public IP
var localIP = await NetworkInterface.DetectLocalIPAddressBasedOnRoutingAsync(IPAddress.Parse(GlobalStaticConfiguration.Dashboard_PublicIPv4Address));

// Could not detect local ip address
// Fallback: Try to detect local IP address from network interfaces -> Prefer non link-local addresses
localIP ??= await NetworkInterface.DetectLocalIPAddressFromNetworkInterfaceAsync(System.Net.Sockets.AddressFamily.InterNetwork);

// If local IP address detected, try to find subnetmask from network interfaces
if (localIP != null)
{
var subnetmaskDetected = false;
var subnetDetected = false;

// Get network interfaces, where local IP address is assigned
var networkInterface = (await NetworkInterface.GetNetworkInterfacesAsync())
.FirstOrDefault(x => x.IPv4Address.Any(y => y.Item1.Equals(localIP)));

// Get subnetmask, based on ip address
foreach (var networkInterface in (await NetworkInterface.GetNetworkInterfacesAsync()).Where(
networkInterface => networkInterface.IPv4Address.Any(x => x.Item1.Equals(localIP))))
// If found, get subnetmask
if (networkInterface != null)
{
subnetmaskDetected = true;

Host = $"{localIP}/{Subnetmask.ConvertSubnetmaskToCidr(networkInterface.IPv4Address.First().Item2)}";
// Find the correct IP address and the associated subnetmask
var ipAddressWithSubnet = networkInterface.IPv4Address.First(x => x.Item1.Equals(localIP));

Host = $"{ipAddressWithSubnet.Item1}/{Subnetmask.ConvertSubnetmaskToCidr(ipAddressWithSubnet.Item2)}";

subnetDetected = true;

// Fix: If the user clears the TextBox and then clicks again on the button, the TextBox remains empty...
OnPropertyChanged(nameof(Host));

break;
}

if (!subnetmaskDetected)
// Show error message if subnet could not be detected
if (!subnetDetected)
{
var window = Application.Current.Windows.OfType<Window>().FirstOrDefault(x => x.IsActive);

Expand Down Expand Up @@ -716,8 +729,9 @@ public void OnClose()
private void HostScanned(object sender, IPScannerHostScannedArgs e)
{
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action(delegate {
Results.Add(e.Args);
new Action(delegate
{
Results.Add(e.Args);
}));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -901,7 +901,7 @@ await NetworkInterface.DetectLocalIPAddressBasedOnRoutingAsync(
Log.Debug("CheckConnectionRouterAsync - Computer IPv4 address detected: " + detectedLocalIPv4Address);

var detectedRouterIPv4 =
await NetworkInterface.DetectGatewayBasedOnLocalIPAddressAsync(
await NetworkInterface.DetectGatewayFromLocalIPAddressAsync(
detectedLocalIPv4Address);

if (detectedRouterIPv4 != null)
Expand Down Expand Up @@ -944,7 +944,7 @@ await NetworkInterface.DetectLocalIPAddressBasedOnRoutingAsync(
Log.Debug("CheckConnectionRouterAsync - Computer IPv6 address detected: " + detectedComputerIPv6);

var detectedRouterIPv6 =
await NetworkInterface.DetectGatewayBasedOnLocalIPAddressAsync(detectedComputerIPv6);
await NetworkInterface.DetectGatewayFromLocalIPAddressAsync(detectedComputerIPv6);

if (detectedRouterIPv6 != null)
{
Expand Down
11 changes: 10 additions & 1 deletion Website/docs/application/ip-scanner.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,16 @@ Example: `10.0.0.0/24; 10.0.[10-20]1`

:::

The button to the left of the `Scan` button determines the IP address and subnet mask of the network interface currently in use in order to scan the local subnet for active devices.
The button to the left of the `Scan` button determines the IP address and subnet mask of the network interface
currently in use in order to scan the local subnet for active devices.

:::note

The local IP address (and subnet mask) is determined by trying to route to a public IP address. If this fails (e.g.
no network connection), NETworkManager iterates over active network adapters and selects the first valid IPv4 address,
with link-local addresses (`169.254.x.x`) given lower priority.

:::

If you right-click on a selected result, you can forward the device information to other applications (e.g. Port Scanner, Remote Desktop, etc), create a new profile with this information or execute a [custom command](#custom-commands).

Expand Down
4 changes: 4 additions & 0 deletions Website/docs/changelog/next-release.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ Release date: **xx.xx.2025**
- Settings format migrated from `XML` to `JSON`. The settings file will be automatically converted on first start after the update. [#3282](https://github.com/BornToBeRoot/NETworkManager/pull/3282)
- Create a daily backup of the settings file before saving changes. Up to `10` backup files are kept in the `Backups` subfolder of the settings directory. [#3283](https://github.com/BornToBeRoot/NETworkManager/pull/3283)

**IP Scanner**

- Improved local subnet detection: If local IP cannot be determined via routing, now iterates over active network adapters and selects the first valid IPv4 address (with link-local addresses (`169.254.x.x`) given lower priority). [#3288](https://github.com/BornToBeRoot/NETworkManager/pull/3288)

**DNS Lookup**

- Allow hostname as server address in addition to IP address in the add/edit server dialog. [#3261](https://github.com/BornToBeRoot/NETworkManager/pull/3261)
Expand Down