/// <summary> /// Parse /// </summary> /// <param name="value"></param> /// <returns></returns> public static IEnumerable <AddressRange> Parse(string value) { if (string.IsNullOrEmpty(value)) { throw new ArgumentNullException(nameof(value)); } var split = value.Split(new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries); return(split .SelectMany(s => { var x = s.Split('/'); if (x.Length != 2) { throw new FormatException("Bad suffix format"); } var suffix = int.Parse(x[1]); if (suffix == 0 || suffix > 32) { throw new FormatException("Bad suffix value"); } if (x[0] == "*") { return NetworkInformationEx.GetAllNetInterfaces( NetworkClass.Wired) .Select(t => new AddressRange(t, false, suffix)); } return new AddressRange(IPAddress.Parse(x[0]), suffix) .YieldReturn(); }).Distinct().ToList()); }
/// <summary> /// Create scanner /// </summary> /// <param name="logger"></param> /// <param name="replies"></param> /// <param name="local"></param> /// <param name="addresses"></param> /// <param name="netclass"></param> /// <param name="maxProbeCount"></param> /// <param name="timeout"></param> /// <param name="ct"></param> public NetworkScanner(ILogger logger, Action <NetworkScanner, PingReply> replies, bool local, IEnumerable <AddressRange> addresses, NetworkClass netclass, int?maxProbeCount, TimeSpan?timeout, CancellationToken ct) { _logger = logger; _replies = replies; _ct = ct; _timeout = timeout ?? kDefaultProbeTimeout; _completion = new TaskCompletionSource <bool>(); _candidates = new List <uint>(); if (addresses == null) { _addresses = NetworkInformationEx.GetAllNetInterfaces(netclass) .Select(t => new AddressRange(t, local)).Distinct().ToList(); } else { _addresses = addresses.Select(a => a.Copy()).Distinct().ToList(); } _pings = CreatePings(local ? _addresses.Count + 1 : maxProbeCount ?? kDefaultMaxProbeCount); // Start initial pings _logger.Information("Start scanning {addresses}...", _addresses.Select(a => a.ToString())); foreach (var ping in _pings.ToList()) { OnNextPing(ping); } }
/// <summary> /// Create request wrapper /// </summary> /// <param name="request"></param> /// <param name="networkClass"></param> public DiscoveryRequest(DiscoveryRequestModel request, NetworkClass networkClass = NetworkClass.Wired) { Request = request?.Clone() ?? throw new ArgumentNullException(nameof(request)); NetworkClass = networkClass; if (!string.IsNullOrEmpty(request.Configuration?.AddressRangesToScan)) { if (AddressRange.TryParse(request.Configuration?.AddressRangesToScan, out var addresses)) { AddressRanges = addresses; } } if (AddressRanges == null) { if (request.Discovery == DiscoveryMode.Fast) { var interfaces = NetworkInformationEx.GetAllNetInterfaces(NetworkClass.Wired); AddressRanges = interfaces.Select(t => new AddressRange(t, false, 24)); AddressRanges = AddressRanges.Concat(interfaces .Where(t => t.Gateway != null && !t.Gateway.Equals(System.Net.IPAddress.Any) && !t.Gateway.Equals(System.Net.IPAddress.None)) .Select(i => new AddressRange(i.Gateway, 32))); } } if (!string.IsNullOrEmpty(request.Configuration?.PortRangesToScan)) { if (PortRange.TryParse(request.Configuration?.PortRangesToScan, out var ports)) { PortRanges = ports; } } if (PortRanges == null) { switch (request.Discovery) { case DiscoveryMode.Local: PortRanges = PortRange.All; break; case DiscoveryMode.Fast: PortRanges = PortRange.WellKnown; break; case DiscoveryMode.Scan: PortRanges = PortRange.Unassigned; break; default: PortRanges = PortRange.OpcUa; break; } } }
/// <summary> /// Parse /// </summary> /// <param name="value"></param> /// <returns></returns> public static IEnumerable <AddressRange> Parse(string value) { if (string.IsNullOrEmpty(value)) { throw new ArgumentNullException(nameof(value)); } var split = value.Split(new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries); var unmerged = split .SelectMany(s => { var nic = string.Empty; var x = s.Split('[', StringSplitOptions.RemoveEmptyEntries); if (x.Length > 1) { var postFix = x[1].Split(']'); if (postFix.Length > 1) { nic = postFix[0]; } s = x[0].Trim(); } x = s.Split('/', StringSplitOptions.RemoveEmptyEntries); if (x.Length != 2) { x = s.Split('-', StringSplitOptions.RemoveEmptyEntries); if (x.Length != 2) { throw new FormatException("Bad suffix format"); } // Combine into cidr ranges and parse so we get distinct ranges return(Parse(new AddressRange( new IPv4Address(IPAddress.Parse(x[0])), new IPv4Address(IPAddress.Parse(x[1])), nic).ToString())); } var suffix = int.Parse(x[1]); if (suffix == 0 || suffix > 32) { throw new FormatException("Bad suffix value"); } if (x[0] == "*") { return(NetworkInformationEx.GetAllNetInterfaces( NetworkClass.Wired) .Select(t => new AddressRange(t, false, suffix))); } return(new AddressRange(IPAddress.Parse(x[0]), suffix, nic) .YieldReturn()); }) .Distinct() .ToList(); return(Merge(unmerged)); }
public void TestSubnetNicLocal() { var nics = NetworkInformationEx.GetAllNetInterfaces(NetworkClass.Wired); if (!nics.Any()) { return; } var nic = nics.First(); var range = new AddressRange(nic, true); Assert.Equal(nic.UnicastAddress, (IPv4Address)range.Low); Assert.Equal(nic.UnicastAddress, (IPv4Address)range.High); Assert.Equal(1, range.Count); }
public void TestSubnetNic() { var nics = NetworkInformationEx.GetAllNetInterfaces(NetworkClass.Wired); if (!nics.Any()) { return; } var nic = nics.First(); var expected = new AddressRange(nic.UnicastAddress, nic.SubnetMask); var range = new AddressRange(nic); Assert.Equal((IPv4Address)expected.Low, (IPv4Address)range.Low); Assert.Equal((IPv4Address)expected.High, (IPv4Address)range.High); Assert.Equal(expected.Count, range.Count); }
/// <inheritdoc/> public async Task <IPublisherClient> ConnectAsync() { const string kPublisherName = "opcpublisher"; const int kPublisherPort = 62222; _logger.Information("Finding publisher to use as publish service..."); var deviceId = _identity.DeviceId; if (string.IsNullOrEmpty(deviceId)) { // No identity at this point - look up from IoT Edge environment deviceId = Environment.GetEnvironmentVariable("IOTEDGE_DEVICEID"); } if (!string.IsNullOrEmpty(deviceId)) { _logger.Debug("Get all modules in current edge context."); // Get modules var modules = await _modules.GetModulesAsync(deviceId); var publisherModule = modules .Where(m => m.Status.EqualsIgnoreCase("running")) .FirstOrDefault(m => m.ImageName?.Contains("opc-publisher") ?? (false || m.Id.EqualsIgnoreCase(kPublisherName))); if (publisherModule == null) { _logger.Warning("No publisher module running in edge context."); } // Have publisher module var moduleId = publisherModule?.Id ?? kPublisherName; _logger.Information("Testing publisher module {moduleId} using methods...", moduleId); var publisher = new PublisherMethodClient(_methods, deviceId, moduleId, _logger); var error = await TestConnectivityAsync(publisher); if (error == null) { _logger.Information( "Success - using publisher module '{moduleId}' ({image}) via methods.", moduleId, publisherModule?.ImageName ?? "<unknown>"); return(publisher); } _logger.Debug("Publisher module {moduleId} method call uncuccessful." + " Fallback to UA server...", moduleId, error); } using (var cts = new CancellationTokenSource()) { // Try shortcut of finding it on default publisher edge module var edgeUri = new Uri($"opc.tcp://{kPublisherName}:{kPublisherPort}"); var edgeEndpoints = await _discovery.FindEndpointsAsync(edgeUri, null, cts.Token); if (edgeEndpoints.Any()) { #if !TEST_PNP_SCAN var publisher = new PublisherServerClient(_client, edgeUri, _logger); var error = await TestConnectivityAsync(publisher); if (error == null) { _logger.Information("Using publisher server on localhost."); return(publisher); } #endif } // Try shortcut of finding it on localhost var uri = new Uri($"opc.tcp://{Utils.GetHostName()}:{kPublisherPort}"); var localEndpoints = await _discovery.FindEndpointsAsync(uri, null, cts.Token); if (localEndpoints.Any()) { #if !TEST_PNP_SCAN var publisher = new PublisherServerClient(_client, uri, _logger); var error = await TestConnectivityAsync(publisher); if (error == null) { _logger.Information("Using publisher server on localhost."); return(publisher); } #endif } // Discover publishers in network - use fast scanning _logger.Information("Try finding publishers in module network..."); var addresses = new List <IPAddress>(); using (var netscanner = new NetworkScanner(_logger, (_, reply) => addresses.Add(reply.Address), false, NetworkInformationEx.GetAllNetInterfaces(NetworkClass.Wired) .Select(t => new AddressRange(t, false, 24)), NetworkClass.Wired, 1000, // TODO: make configurable - intent is fast. null, cts.Token)) { await netscanner.Completion; } var publishers = new List <IPEndPoint>(); var probe = new ServerProbe(_logger); using (var portscan = new PortScanner(_logger, addresses.Select(a => new IPEndPoint(a, kPublisherPort)), (_, found) => { cts.Cancel(); // Cancel on first found publisher. publishers.Add(found); }, probe, null, null, null, cts.Token)) { try { await portscan.Completion; } catch (TaskCanceledException) { // We got a port, and scanning is cancelled. } } // We might have found a couple publishers - lets test them... foreach (var ep in publishers) { var remoteEp = await ep.TryResolveAsync(); _logger.Information("Test publisher at address {remoteEp} in network.", remoteEp); uri = new Uri("opc.tcp://" + remoteEp); var endpoints = await _discovery.FindEndpointsAsync(uri, null, CancellationToken.None); if (!endpoints.Any()) { continue; } var publisher = new PublisherServerClient(_client, uri, _logger); var error = await TestConnectivityAsync(publisher); if (error == null) { _logger.Information("Using publisher server at address {remoteEp}.", remoteEp); return(publisher); } } // TODO: Consider loading publisher as side car service? // No publisher found - will try again later. return(null); } }
/// <summary> /// Create request wrapper /// </summary> /// <param name="request"></param> /// <param name="networkClass"></param> /// <param name="isScan"></param> public DiscoveryRequest(DiscoveryRequestModel request, NetworkClass networkClass = NetworkClass.Wired, bool isScan = false) { Request = request?.Clone() ?? throw new ArgumentNullException(nameof(request)); _cts = new CancellationTokenSource(); NetworkClass = networkClass; IsScan = isScan; if (Request.Configuration == null) { Request.Configuration = new DiscoveryConfigModel(); } if (Request.Discovery == null || Request.Discovery == DiscoveryMode.Off) { // Report empty configuration if off, but keep the // discovery urls details from the original request Request.Configuration = new DiscoveryConfigModel() { ActivationFilter = Request.Configuration.ActivationFilter?.Clone(), DiscoveryUrls = Request.Configuration.DiscoveryUrls?.ToList(), Locales = Request.Configuration.Locales?.ToList() }; Request.Discovery = DiscoveryMode.Off; return; } // Parse whatever provided if (!string.IsNullOrEmpty(Request.Configuration.PortRangesToScan)) { if (PortRange.TryParse(Request.Configuration.PortRangesToScan, out var ports)) { PortRanges = ports; if (Request.Discovery == null) { Request.Discovery = DiscoveryMode.Fast; } } } if (!string.IsNullOrEmpty(Request.Configuration.AddressRangesToScan)) { if (AddressRange.TryParse(Request.Configuration.AddressRangesToScan, out var addresses)) { AddressRanges = addresses; if (Request.Discovery == null) { Request.Discovery = DiscoveryMode.Fast; } } } // Set default ranges if (AddressRanges == null) { IEnumerable <NetInterface> interfaces; switch (Request.Discovery) { case DiscoveryMode.Local: interfaces = NetworkInformationEx.GetAllNetInterfaces(NetworkClass); AddressRanges = AddLocalHost(interfaces .Select(t => new AddressRange(t, true))) .Distinct(); break; case DiscoveryMode.Fast: interfaces = NetworkInformationEx.GetAllNetInterfaces(NetworkClass.Wired); AddressRanges = AddLocalHost(interfaces .Select(t => new AddressRange(t, false, 24)) .Concat(interfaces .Where(t => t.Gateway != null && !t.Gateway.Equals(IPAddress.Any) && !t.Gateway.Equals(IPAddress.None)) .Select(i => new AddressRange(i.Gateway, 32))) .Distinct()); break; case DiscoveryMode.Network: case DiscoveryMode.Scan: interfaces = NetworkInformationEx.GetAllNetInterfaces(NetworkClass); AddressRanges = AddLocalHost(interfaces .Select(t => new AddressRange(t, false)) .Concat(interfaces .Where(t => t.Gateway != null && !t.Gateway.Equals(IPAddress.Any) && !t.Gateway.Equals(IPAddress.None)) .Select(i => new AddressRange(i.Gateway, 32))) .Distinct()); break; case DiscoveryMode.Off: default: AddressRanges = Enumerable.Empty <AddressRange>(); break; } } if (PortRanges == null) { switch (Request.Discovery) { case DiscoveryMode.Local: PortRanges = PortRange.All; break; case DiscoveryMode.Fast: PortRanges = PortRange.WellKnown; break; case DiscoveryMode.Scan: PortRanges = PortRange.Unassigned; break; case DiscoveryMode.Network: PortRanges = PortRange.OpcUa; break; case DiscoveryMode.Off: default: PortRanges = Enumerable.Empty <PortRange>(); break; } } // Update reported configuration with used settings if (AddressRanges != null && AddressRanges.Any()) { Request.Configuration.AddressRangesToScan = AddressRange.Format(AddressRanges); TotalAddresses = AddressRanges?.Sum(r => r.Count) ?? 0; } if (PortRanges != null && PortRanges.Any()) { Request.Configuration.PortRangesToScan = PortRange.Format(PortRanges); TotalPorts = PortRanges?.Sum(r => r.Count) ?? 0; } Request.Configuration.IdleTimeBetweenScans ??= kDefaultIdleTime; Request.Configuration.PortProbeTimeout ??= kDefaultPortProbeTimeout; Request.Configuration.NetworkProbeTimeout ??= kDefaultNetworkProbeTimeout; }
/// <summary> /// Create request wrapper /// </summary> /// <param name="request"></param> /// <param name="networkClass"></param> /// <param name="isScan"></param> public DiscoveryRequest(DiscoveryRequestModel request, NetworkClass networkClass = NetworkClass.Wired, bool isScan = false) { Request = request?.Clone() ?? throw new ArgumentNullException(nameof(request)); _cts = new CancellationTokenSource(); NetworkClass = networkClass; IsScan = isScan; if (request == null) { request = new DiscoveryRequestModel { Discovery = DiscoveryMode.Off }; } if (request.Configuration == null) { request.Configuration = new DiscoveryConfigModel(); } if (!string.IsNullOrEmpty(request.Configuration.AddressRangesToScan)) { if (AddressRange.TryParse(request.Configuration.AddressRangesToScan, out var addresses)) { AddressRanges = addresses; } } if (AddressRanges == null) { switch (request.Discovery) { case DiscoveryMode.Local: AddressRanges = NetworkInformationEx.GetAllNetInterfaces(NetworkClass) .Select(t => new AddressRange(t, true)).Distinct(); break; case DiscoveryMode.Fast: var interfaces = NetworkInformationEx.GetAllNetInterfaces(NetworkClass.Wired); AddressRanges = interfaces.Select(t => new AddressRange(t, false, 24)); AddressRanges = AddressRanges.Concat(interfaces .Where(t => t.Gateway != null && !t.Gateway.Equals(System.Net.IPAddress.Any) && !t.Gateway.Equals(System.Net.IPAddress.None)) .Select(i => new AddressRange(i.Gateway, 32))); break; case DiscoveryMode.Off: AddressRanges = Enumerable.Empty <AddressRange>(); break; case DiscoveryMode.Scan: AddressRanges = NetworkInformationEx.GetAllNetInterfaces(NetworkClass) .Select(t => new AddressRange(t, false)).Distinct(); break; default: AddressRanges = Enumerable.Empty <AddressRange>(); break; } } request.Configuration.AddressRangesToScan = AddressRange.Format(AddressRanges); if (!string.IsNullOrEmpty(request.Configuration.PortRangesToScan)) { if (PortRange.TryParse(request.Configuration.PortRangesToScan, out var ports)) { PortRanges = ports; } } if (PortRanges == null) { switch (request.Discovery) { case DiscoveryMode.Local: PortRanges = PortRange.All; break; case DiscoveryMode.Fast: PortRanges = PortRange.WellKnown; break; case DiscoveryMode.Scan: PortRanges = PortRange.Unassigned; break; case DiscoveryMode.Off: PortRanges = Enumerable.Empty <PortRange>(); break; default: PortRanges = PortRange.OpcUa; break; } } request.Configuration.PortRangesToScan = PortRange.Format(PortRanges); request.Configuration.IdleTimeBetweenScans ??= kDefaultIdleTime; request.Configuration.PortProbeTimeout ??= kDefaultPortProbeTimeout; request.Configuration.NetworkProbeTimeout ??= kDefaultNetworkProbeTimeout; TotalAddresses = AddressRanges?.Sum(r => r.Count) ?? 0; TotalPorts = PortRanges?.Sum(r => r.Count) ?? 0; }