Skip to content

Commit

Permalink
Fix peer discovery requests with multiple NICs
Browse files Browse the repository at this point in the history
When multiple functional network interfaces were available in a system, only one of them would be used to send peer discovery request messages. If that happened to be a virtual interface, no discovery messages would ever be sent from the real network card to the real network.

Now the code will loop through all network interfaces with multicast capability and send the discovery message from each of them. Duplicate responses will be ignored, in case multiple interfaces are on the same network and receive the same responses.

The same applies to listening for peer discovery requests, the server will now listen on all interfaces.
  • Loading branch information
YorVeX committed Dec 7, 2023
1 parent 9cf3208 commit 45a7fae
Show file tree
Hide file tree
Showing 5 changed files with 261 additions and 103 deletions.
2 changes: 1 addition & 1 deletion src/BeamReceiver.cs
Expand Up @@ -87,7 +87,7 @@ public async Task ConnectAsync(IPAddress bindAddress, string hostname, int port,
// do peer discovery if discovery information is available
if (!currentPeer.IsEmpty)
{
var discoveredPeers = PeerDiscovery.Discover(currentPeer).Result;
var discoveredPeers = PeerDiscovery.Discover(currentPeer);
if (discoveredPeers.Count > 0)
{
_targetHostname = discoveredPeers[0].IP;
Expand Down
4 changes: 2 additions & 2 deletions src/BeamSenderProperties.cs
Expand Up @@ -189,7 +189,7 @@ public unsafe IPAddress NetworkInterfaceAddress
if (configuredNetworkInterfaceName == "Any: 0.0.0.0")
return IPAddress.Any;

foreach (var networkInterface in NetworkInterfaces.GetAllNetworkInterfaces())
foreach (var networkInterface in NetworkInterfaces.AllNetworkInterfaces)
{
foreach (var ip in networkInterface.GetIPProperties().UnicastAddresses)
{
Expand Down Expand Up @@ -514,7 +514,7 @@ private unsafe void Initialize(obs_data* settings)
fixed (byte* networkInterfaceAnyListItem = "Any: 0.0.0.0"u8)
ObsProperties.obs_property_list_add_string(networkInterfacesListProperty, (sbyte*)networkInterfaceAnyListItem, (sbyte*)networkInterfaceAnyListItem);
NetworkInterfacesHaveLocalAddress = false;
foreach (var networkInterface in NetworkInterfaces.GetAllNetworkInterfaces())
foreach (var networkInterface in NetworkInterfaces.AllNetworkInterfaces)
{
if (networkInterface.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up)
{
Expand Down
67 changes: 53 additions & 14 deletions src/NetworkInterfaces.cs
Expand Up @@ -20,7 +20,50 @@ public static partial class NetworkInterfaces

private static NetworkInterface[] _networkInterfaces = [];
private static List<(UnicastIPAddressInformation, string)> _unicastAddressesWithIds = [];
private static object _networkInterfacesLock = new();
private static List<IPAddress> _multicastInterfaceIps = [];
private static readonly object _networkInterfacesLock = new();

public static NetworkInterface[] AllNetworkInterfaces
{
get
{
lock (_networkInterfacesLock)
return _networkInterfaces;
}
private set
{
lock (_networkInterfacesLock)
_networkInterfaces = value;
}
}

public static List<IPAddress> MulticastInterfaceIps
{
get
{
lock (_networkInterfacesLock)
return _multicastInterfaceIps;
}
private set
{
lock (_networkInterfacesLock)
_multicastInterfaceIps = value;
}
}

public static List<(UnicastIPAddressInformation, string)> UnicastAddressesWithIds
{
get
{
lock (_networkInterfacesLock)
return _unicastAddressesWithIds;
}
private set
{
lock (_networkInterfacesLock)
_unicastAddressesWithIds = value;
}
}

// this is not called before the first method from this class was used, meaning the first call to GetAllNetworkInterfaces() or GetUnicastAddressesWithIds() will implicitly invoke this
static NetworkInterfaces()
Expand All @@ -41,22 +84,29 @@ static NetworkInterfaces()
public static void UpdateNetworkInterfaces()
{
var networkInterfacesWithIds = new List<(UnicastIPAddressInformation, string)>();
var multicastInterfaceIps = new List<IPAddress>();
Module.Log($"Refreshing list of network interfaces...", ObsLogLevel.Debug);
var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
foreach (var networkInterface in networkInterfaces)
{
if (networkInterface.OperationalStatus == OperationalStatus.Up)
{
foreach (var ip in networkInterface.GetIPProperties().UnicastAddresses)
var ipProperties = networkInterface.GetIPProperties();
foreach (var ip in ipProperties.UnicastAddresses)
{
if (ip.Address.AddressFamily == AddressFamily.InterNetwork)
{
// remember working unicast addresses (this includes localhost and virtual interfaces)
string identifierString = networkInterface.NetworkInterfaceType == NetworkInterfaceType.Loopback ? "localhost" : networkInterface.GetPhysicalAddress().ToString();
if (string.IsNullOrEmpty(identifierString))
identifierString = networkInterface.Name;
string hashIdentifier = BitConverter.ToString(System.Security.Cryptography.SHA256.HashData(Encoding.UTF8.GetBytes(identifierString))).Replace("-", "");
networkInterfacesWithIds.Add((ip, hashIdentifier));
// Module.Log("NIC: \"" + networkInterface.Name + "\": " + ip.Address + " / " + identifierString + " / " + hashIdentifier, ObsLogLevel.Debug);

// remember multicast capable interfaces (this does not include localhost and virtual interfaces)
if ((networkInterface.NetworkInterfaceType != NetworkInterfaceType.Loopback) && networkInterface.SupportsMulticast && (ipProperties.MulticastAddresses.Count > 0))
multicastInterfaceIps.Add(ip.Address);
}
}
}
Expand All @@ -65,22 +115,11 @@ public static void UpdateNetworkInterfaces()
{
_networkInterfaces = networkInterfaces;
_unicastAddressesWithIds = networkInterfacesWithIds;
_multicastInterfaceIps = multicastInterfaceIps;
}
Module.Log($"Refreshing list of network interfaces done.", ObsLogLevel.Debug);
}

public static NetworkInterface[] GetAllNetworkInterfaces()
{
lock (_networkInterfacesLock)
return _networkInterfaces;
}

public static List<(UnicastIPAddressInformation, string)> GetUnicastAddressesWithIds()
{
lock (_networkInterfacesLock)
return _unicastAddressesWithIds;
}

// source: https://regex101.com/r/JCLOZL/15
[GeneratedRegex(@"\b(127\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|0?10\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|172\.0?1[6-9]\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|172\.0?2[0-9]\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|172\.0?3[01]\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|192\.168\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|169\.254\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|::1|[fF][cCdD][0-9a-fA-F]{2}(?:[:][0-9a-fA-F]{0,4}){0,7}|[fF][eE][89aAbB][0-9a-fA-F](?:[:][0-9a-fA-F]{0,4}){0,7})(?:\/([789]|1?[0-9]{2}))?\b")]
private static partial Regex RegexLocalAddress();
Expand Down

0 comments on commit 45a7fae

Please sign in to comment.