Ejemplo n.º 1
0
        public async Task <IActionResult> Kml(string fromSite, string toSite, [FromQuery] FromUrlQueryQuerierOptions options, [FromQuery] KmlSettingsFromQuery kmlSettings)
        {
            Program.RequestStatistics.ApiV1KmlRequests++;

            if (string.IsNullOrWhiteSpace(fromSite))
            {
                return(this.BadRequest("Error: fromSite is null, empty or white-space-only"));
            }

            if (string.IsNullOrWhiteSpace(toSite))
            {
                return(this.BadRequest("Error: toSite is null, empty or white-space-only"));
            }

            IQuerierOptions optionsInUse = this.CreateOptions(options);

            try
            {
                var action    = new KmlAction(WebUtility.UrlDecode(fromSite), WebUtility.UrlDecode(toSite), optionsInUse as FromUrlQueryQuerierOptions, this.hamnetDbAccess);
                var kmlString = await action.Execute();

                if ((kmlSettings != null) && (kmlSettings.AsText))
                {
                    return(this.Ok(kmlString));
                }

                return(this.File(Encoding.UTF8.GetBytes(kmlString), KmlContentType, $"{fromSite}-{toSite}-{DateTime.Now:yyyyMMddTHHmmss}.kml"));
            }
            catch (Exception ex)
            {
                return(this.BadRequest($"Error: {ex.Message}"));
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Construct from address and options.
        /// </summary>
        /// <param name="address">The address to talk to.</param>
        /// <param name="options">The options to use.</param>
        public SnmpLowerLayer(IpAddress address, IQuerierOptions options)
        {
            this.Address = address ?? throw new ArgumentNullException(nameof(address), "address to talk to is null");
            this.Options = options ?? QuerierOptions.Default;

            this.SetVersionAndCommunity(this.Options.ProtocolVersion, this.Options.Community);
        }
Ejemplo n.º 3
0
        public async Task <IActionResult> KmlFromCallToRaw([FromQuery] FromUrlQueryQuerierOptions options, [FromQuery] ToLocationFromQuery toLocation, [FromQuery] FromLocationFromQuery fromLocation, [FromQuery] KmlSettingsFromQuery kmlSettings)
        {
            Program.RequestStatistics.ApiV1KmlRequests++;

            if (fromLocation == null)
            {
                return(this.BadRequest("Error: fromLocation is null"));
            }

            if (toLocation == null)
            {
                return(this.BadRequest("Error: location is null"));
            }

            IQuerierOptions optionsInUse = this.CreateOptions(options);

            try
            {
                var action    = new KmlAction(fromLocation, toLocation, optionsInUse as FromUrlQueryQuerierOptions, this.hamnetDbAccess);
                var kmlString = await action.Execute();

                if ((kmlSettings != null) && (kmlSettings.AsText))
                {
                    return(this.Ok(kmlString));
                }

                return(this.File(Encoding.UTF8.GetBytes(kmlString), KmlContentType, $"raw-From-To-{DateTime.Now:yyyyMMddTHHmmss}.kml"));
            }
            catch (Exception ex)
            {
                return(this.BadRequest($"Error: {ex.Message}"));
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Creates a new querier using the given lower layer and options.
        /// </summary>
        /// <param name="lowerLayer">The IP address of the device to query.</param>
        /// <param name="options">The options for the query.</param>
        /// <returns>An <see cref="IHamnetQuerier" /> that talks to the given address.</returns>
        internal IHamnetQuerier Create(ISnmpLowerLayer lowerLayer, IQuerierOptions options)
        {
            if (lowerLayer == null)
            {
                throw new ArgumentNullException(nameof(lowerLayer), "lowerLayer is null");
            }

            IHamnetQuerier querier = null;

            if (options.EnableCaching)
            {
                querier = new CachingHamnetQuerier(lowerLayer, options);
            }
            else
            {
                var detector = new DeviceDetector(lowerLayer);

                IDeviceHandler handler = detector.Detect(options);

                if (handler == null)
                {
                    var errorInfo = $"Cannot obtain a feasible device handler for device '{lowerLayer.Address}'";
                    log.Error(errorInfo);
                    throw new HamnetSnmpException(errorInfo, lowerLayer.Address?.ToString());
                }

                querier = new HamnetQuerier(handler, lowerLayer.Options);
            }

            return(querier);
        }
Ejemplo n.º 5
0
        public async Task <ActionResult <IStatusReply> > TracerouteHost(string fromHost, string toHost, [FromQuery] FromUrlQueryQuerierOptions options, int count = 1, double timeoutSeconds = 1.0, int maxHops = MaxTracerouteMaxHops / 4)
        {
            Program.RequestStatistics.ApiV1TraceRouteRequests++;

            IQuerierOptions optionsInUse = this.CreateOptions(options);

            if ((count < 1) || (count > MaxTracerouteSendCount))
            {
                return(new ErrorReply(new ArgumentOutOfRangeException(nameof(count), $"count must be in range [1; {MaxTracerouteSendCount}] but was found as {count}")));
            }

            if ((timeoutSeconds < MinTracerouteTimeoutSeconds) || (timeoutSeconds > MaxTracerouteTimeoutSeconds))
            {
                return(new ErrorReply(new ArgumentOutOfRangeException(nameof(timeoutSeconds), $"timeoutSeconds must be in range [{MinTracerouteTimeoutSeconds}; {MaxTracerouteTimeoutSeconds}] but was found as {timeoutSeconds}")));
            }

            if ((maxHops < MinTracerouteMaxHops) || (maxHops > MaxTracerouteMaxHops))
            {
                return(new ErrorReply(new ArgumentOutOfRangeException(nameof(maxHops), $"maxHops must be in range [{MinTracerouteMaxHops}; {MaxTracerouteMaxHops}] but was found as {maxHops}")));
            }

            try
            {
                return(await new TracerouteAction(WebUtility.UrlDecode(fromHost), WebUtility.UrlDecode(toHost), count, TimeSpan.FromSeconds(timeoutSeconds), maxHops, optionsInUse as FromUrlQueryQuerierOptions).Execute());
            }
            catch (Exception ex)
            {
                return(this.BadRequest($"Error: {ex.Message}"));
            }
        }
Ejemplo n.º 6
0
        public async Task <ActionResult <IStatusReply> > PingHost(string host, string remoteIp, [FromQuery] FromUrlQueryQuerierOptions options)
        {
            Program.RequestStatistics.ApiV1BgpHostSpecificRequests++;

            IQuerierOptions optionsInUse = this.CreateOptions(options);

            return(await new BgpPeersAction(WebUtility.UrlDecode(host), WebUtility.UrlDecode(remoteIp), optionsInUse as FromUrlQueryQuerierOptions).Execute());
        }
        /// <inheritdoc />
        public override IDeviceHandler CreateHandler(ISnmpLowerLayer lowerLayer, IQuerierOptions options)
        {
            string osVersionString = null;

            // try #1: In IEEE SNMP tree
            try
            {
                osVersionString = lowerLayer.QueryAsString(OsVersionOid, "MikroTik RouterOS Version String #1");
            }
            catch (SnmpException ex)
            {
                this.CollectException("MtikSnmp: Getting version string", ex);
                osVersionString = null;
            }
            catch (HamnetSnmpException ex)
            {
                this.CollectException("MtikSnmp: Getting version string", ex);
                osVersionString = null;
            }

            // try #2: Withing MikroTik enterprise tree
            if (string.IsNullOrWhiteSpace(osVersionString))
            {
                osVersionString = lowerLayer.QueryAsString(OsVersionOid2, "MikroTik RouterOS Version String #2");
            }

            // Example: "RouterOS 6.45.3 (stable) on RB711-5Hn-MMCX"
            Match match = OsVersionExtractionRegex.Match(osVersionString);

            SemanticVersion osVersion = match.Success ? match.Groups[1].Value.ToSemanticVersion() : null;

            var model = lowerLayer.SystemData.Description.Replace(RouterOsDetectionString, string.Empty).Trim();

            log.Info($"Detected device '{lowerLayer.Address}' as MikroTik '{model}' v '{osVersion}'");

            DeviceVersion            deviceVersion;
            IDeviceSpecificOidLookup oidTable = this.ObtainOidTable(model.Trim(), osVersion, out deviceVersion, lowerLayer.Address);

            if (string.IsNullOrWhiteSpace(deviceVersion.HandlerClassName))
            {
                try
                {
                    return(new MikrotikSnmpDeviceHandler(lowerLayer, oidTable, osVersion, model, options));
                }
                catch (Exception ex)
                {
                    this.CollectException("MtikSnmp: OID table lookup", ex);

                    // we want to catch and nest the exception here as the APIs involved are not able to append the infomration for which
                    // device (i.e. IP address) the exception is for
                    throw new HamnetSnmpException($"Failed to create MikroTik handler for device '{lowerLayer.Address}': {ex.Message}", ex, lowerLayer.Address?.ToString());
                }
            }
            else
            {
                return(this.GetHandlerViaReflection(deviceVersion.HandlerClassName, lowerLayer, oidTable, osVersion, model, options));
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Creates a new querier to the given address using the given options.
        /// </summary>
        /// <param name="address">The IP address of the device to query.</param>
        /// <param name="options">The options for the query.</param>
        /// <returns>An <see cref="IHamnetQuerier" /> that talks to the given address.</returns>
        public IHamnetQuerier Create(IPAddress address, IQuerierOptions options)
        {
            if (address == null)
            {
                throw new ArgumentNullException(nameof(address), "address is null");
            }

            ISnmpLowerLayer lowerLayer = new SnmpLowerLayer(new IpAddress(address), options);

            return(this.Create(lowerLayer, options));
        }
Ejemplo n.º 9
0
        /// <inheritdoc />
        public virtual IDeviceHandler CreateHandler(ISnmpLowerLayer lowerLayer, IQuerierOptions options)
        {
            if (this.circularCreateHandler++ > 1)
            {
                var ex = new InvalidOperationException($"Internal Error: DetectableDevice {this.GetType().Name} seems to neither implement CreateHandler(ISnmpLowerLayer lowerLayer, IQuerierOptions options) nor CreateHandler(IpAddress address, IQuerierOptions options)");
                this.CollectException("CreateHandler(ISnmpLowerLayer, IQuerierOptions) circular call", ex);
                throw ex;
            }

            return(this.CreateHandler(lowerLayer.Address, options));
        }
Ejemplo n.º 10
0
        /// <inheritdoc />
        public override IDeviceHandler CreateHandler(IpAddress address, IQuerierOptions options)
        {
            if (this.tikConnection == null)
            {
                var ex = new InvalidOperationException($"Device {address}: No Mikrotik API connection available in CreateHandler. Did you call IsApplicableVendorSpecific and receive back true from that call?");
                this.CollectException("MtikApi: MTik connection in CreateHandler(IpAddress, IQuerierOptions)", ex);
                throw ex;
            }

            return(new MikrotikApiDeviceHandler(address, this.apiInUse, this.tikConnection, options, this.sysIdent, this.sysResource, this.sysRouterboard));
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Construct for a specific host.
        /// </summary>
        /// <param name="host">The host to get the info for.</param>
        /// <param name="logger">The logger to use.</param>
        /// <param name="configuration">The service configuration.</param>
        /// <param name="querierOptions">The options to the Hamnet querier.</param>
        public HostInfo(string host, ILogger logger, IConfiguration configuration, IQuerierOptions querierOptions)
        {
            if (string.IsNullOrWhiteSpace(host))
            {
                throw new ArgumentNullException(nameof(host), "Host to test is null, empty or white-space-only");
            }

            this.logger         = logger;
            this.configuration  = configuration;
            this.host           = host;
            this.querierOptions = querierOptions ?? new FromUrlQueryQuerierOptions();
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Creates a new instance using the specified device handler.
        /// </summary>
        /// <param name="handler">The device handler to use for obtaining SNMP data.</param>
        /// <param name="options">The options for the query.</param>
        public HamnetQuerier(IDeviceHandler handler, IQuerierOptions options)
        {
            if (handler == null)
            {
                throw new ArgumentNullException(nameof(handler), "The device handler is null");
            }

            if (options == null)
            {
                throw new ArgumentNullException(nameof(options), "The query options are null");
            }

            this.handler = handler;
            this.options = options;
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Construct for a specific host.
        /// </summary>
        /// <param name="host1">The first host or IP of the link.</param>
        /// <param name="host2">The second host or IP of the link.</param>
        /// <param name="querierOptions">The options to the Hamnet querier.</param>
        public LinkTest(string host1, string host2, IQuerierOptions querierOptions)
        {
            if (string.IsNullOrWhiteSpace(host1))
            {
                throw new ArgumentNullException(nameof(host1), "Host #1 is null, empty or white-space-only");
            }

            if (string.IsNullOrWhiteSpace(host2))
            {
                throw new ArgumentNullException(nameof(host2), "Host #2 is null, empty or white-space-only");
            }

            this.host1          = host1;
            this.host2          = host2;
            this.querierOptions = querierOptions ?? new FromUrlQueryQuerierOptions();
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Creates a new querier to the given address using the given options and cache settings.
        /// </summary>
        /// <param name="hostNameOrAddress">The host name or IP address of the device to query.</param>
        /// <param name="options">The options for the query.</param>
        /// <returns>An <see cref="IHamnetQuerier" /> that talks to the given address.</returns>
        public IHamnetQuerier Create(string hostNameOrAddress, IQuerierOptions options)
        {
            if (string.IsNullOrWhiteSpace(hostNameOrAddress))
            {
                throw new ArgumentNullException(nameof(hostNameOrAddress), "host name or address is null, empty or white-space-only");
            }

            IPAddress address;

            if (!hostNameOrAddress.TryGetResolvedConnecionIPAddress(out address))
            {
                throw new HamnetSnmpException($"Host name or address '{hostNameOrAddress}' cannot be resolved to a valid IP address", hostNameOrAddress);
            }

            return(this.Create(address, options));
        }
Ejemplo n.º 15
0
        /// <inheritdoc />
        public override bool IsApplicableSnmp(ISnmpLowerLayer snmpLowerLayer, IQuerierOptions options)
        {
            var description = snmpLowerLayer?.SystemData?.Description;

            if (string.IsNullOrWhiteSpace(description))
            {
                var info = $"Description in system data of device '{snmpLowerLayer.Address}' is null, empty or white-space-only: Assuming the device is not a Ubiquiti device";
                log.Warn(info);
                this.CollectException("UbntSnmp: No device description", new HamnetSnmpException(info));
                return(false);
            }

            if (!description.Contains(PossiblyUbiquitiDetectionDescriptionString))
            {
                var info = $"Description in system data of device '{snmpLowerLayer.Address}' doesn't contain string '{PossiblyUbiquitiDetectionDescriptionString}': Assuming the device is not a Ubiquiti device";
                log.Info(info);
                this.CollectException("UbntSnmp: No UBNT-like string in device description", new HamnetSnmpException(info));
                return(false);
            }

            var ubntManufacturer = snmpLowerLayer?.DoWalk(UbntManufacturerDetectionOid);

            if ((ubntManufacturer == null) || (ubntManufacturer.Count == 0))
            {
                var info = $"UBNT Manufacturer ID string of device '{snmpLowerLayer.Address}' is null or empty: The device could still be AirFiber";
                log.Warn(info);
                this.CollectException("UbntSnmp: No UBNT manufacturer detection OID", new HamnetSnmpException(info));
                return(this.DetectAirFiber(snmpLowerLayer));
            }

            var manufacturer = ubntManufacturer.FirstOrDefault(m => m.Value.ToString().Contains(UbiquitiManufactorerDetectionString));

            if (manufacturer == null)
            {
                var info = $"UBNT Manufacturer ID string of device '{snmpLowerLayer.Address}' doesn't contain string '{UbiquitiManufactorerDetectionString}': Assuming the device is not a Ubiquiti device";
                log.Info(info);
                this.CollectException("UbntSnmp: No UBNT manufacturer", new HamnetSnmpException(info));
                return(false);
            }

            this.detectionId = manufacturer.Oid[manufacturer.Oid.Length - 1];

            log.Info($"Device '{snmpLowerLayer.Address}' seems to be a Ubiquiti device (detection ID {this.detectionId})");

            return(true);
        }
Ejemplo n.º 16
0
        /// <summary>
        /// Construct for a specific host.
        /// </summary>
        /// <param name="network">The network to test in CIDR or IP/Netmask notation.</param>
        /// <param name="logger">The logger to use.</param>
        /// <param name="hamnetDbAccess">The service configuration.</param>
        /// <param name="querierOptions">The options to the Hamnet querier.</param>
        public NetworkTest(string network, ILogger logger, IHamnetDbAccess hamnetDbAccess, IQuerierOptions querierOptions)
        {
            if (string.IsNullOrWhiteSpace(network))
            {
                throw new ArgumentNullException(nameof(network), "Network to test is null, empty or white-space-only");
            }

            IPNetwork subnet = null;

            if (!IPNetwork.TryParse(network, out subnet))
            {
                throw new ArgumentException($"Specified network '{network}' is not a valid IP network specification", nameof(network));
            }

            this.logger         = logger;
            this.hamnetDbAccess = hamnetDbAccess ?? throw new ArgumentNullException(nameof(hamnetDbAccess), "Handle to the HamnetDB accessor is null");
            this.network        = subnet;
            this.querierOptions = querierOptions ?? new FromUrlQueryQuerierOptions();
        }
Ejemplo n.º 17
0
        /// <inheritdoc />
        public override bool IsApplicableSnmp(ISnmpLowerLayer snmpLowerLayer, IQuerierOptions options)
        {
            var description = snmpLowerLayer?.SystemData?.Description;

            if (string.IsNullOrWhiteSpace(description))
            {
                var info = $"Description in system data of device '{snmpLowerLayer.Address}' is null, empty or white-space-only: Assuming the device is not a MikroTik device";
                this.CollectException("AlixSnmp: No device description", new HamnetSnmpException(info));
                log.Warn(info);
                return(false);
            }

            if (!AlixDetectionStrings.Any(ds => description.Contains(ds, StringComparison.InvariantCultureIgnoreCase)))
            {
                var info = $"Description in system data of device '{snmpLowerLayer.Address}' doesn't contain any of the strings string '{string.Join(", ", AlixDetectionStrings)}': Assuming the device is not an ALIX device";
                this.CollectException("AlixSnmp: No ALIX-like string in device description", new HamnetSnmpException(info));
                log.Info(info);
                return(false);
            }

            log.Info($"Device '{snmpLowerLayer.Address}' seems to be an ALIX device");

            return(true);
        }
Ejemplo n.º 18
0
        /// <inheritdoc />
        public override IDeviceHandler CreateHandler(ISnmpLowerLayer lowerLayer, IQuerierOptions options)
        {
            string osVersionString = "0.0.0";

            // Example: "..."
            Match match = OsVersionExtractionRegex.Match(osVersionString);

            SemanticVersion osVersion = osVersionString.ToSemanticVersion(); // match.Success ? match.Groups[1].Value.ToSemanticVersion() : null;

            var model = lowerLayer.SystemData.Description;                   //lowerLayer.SystemData.Description.Replace(RouterOsDetectionString, string.Empty).Trim();

            log.Info($"Detected device '{lowerLayer.Address}' as ALIX '{model}' v '{osVersion}'");

            DeviceVersion            deviceVersion;
            IDeviceSpecificOidLookup oidTable = this.ObtainOidTable(model.Trim(), osVersion, out deviceVersion, lowerLayer.Address);

            if (string.IsNullOrWhiteSpace(deviceVersion.HandlerClassName))
            {
                try
                {
                    return(new AlixDeviceHandler(lowerLayer, oidTable, osVersion, model, options));
                }
                catch (Exception ex)
                {
                    this.CollectException("AlixSnmp: OID table lookup", ex);

                    // we want to catch and nest the exception here as the APIs involved are not able to append the infomration for which
                    // device (i.e. IP address) the exception is for
                    throw new HamnetSnmpException($"Failed to create ALIX handler for device '{lowerLayer.Address}': {ex.Message}", ex, lowerLayer?.Address?.ToString());
                }
            }
            else
            {
                return(this.GetHandlerViaReflection(deviceVersion.HandlerClassName, lowerLayer, oidTable, osVersion, model, options));
            }
        }
        /// <inheritdoc />
        public override bool IsApplicableSnmp(ISnmpLowerLayer snmpLowerLayer, IQuerierOptions options)
        {
            var description = snmpLowerLayer?.SystemData?.Description;

            if (string.IsNullOrWhiteSpace(description))
            {
                var info = $"Description in system data of device '{snmpLowerLayer.Address}' is null, empty or white-space-only: Assuming the device is not a MikroTik device";
                log.Warn(info);
                this.CollectException("MtikSnmp: No device description", new HamnetSnmpException(info));
                return(false);
            }

            if (!description.Contains(RouterOsDetectionString))
            {
                var info = $"Description in system data of device '{snmpLowerLayer.Address}' doesn't contain string '{RouterOsDetectionString}': Assuming the device is not a MikroTik device";
                log.Info(info);
                this.CollectException("MtikSnmp: No Router-OS-like string in device description", new HamnetSnmpException(info));
                return(false);
            }

            log.Info($"Device '{snmpLowerLayer.Address}' seems to be a MikroTik device");

            return(true);
        }
Ejemplo n.º 20
0
        /// <summary>
        /// Constructs for the given lower layer.
        /// </summary>
        /// <param name="lowerLayer">The lower layer for talking to this device.</param>
        /// <param name="oidLookup">The OID lookup table for the device.</param>
        /// <param name="osVersion">The SW version of the device.</param>
        /// <param name="model">The device's model name. Shall be the same name as used for the device name during OID database lookups.</param>
        /// <param name="options">The options to use.</param>
        public AlixDeviceHandler(ISnmpLowerLayer lowerLayer, IDeviceSpecificOidLookup oidLookup, SemanticVersion osVersion, string model, IQuerierOptions options)
            : base(lowerLayer, oidLookup, osVersion, model, options)
        {
            if ((options.AllowedApis & this.SupportedApi) == 0)
            {
                throw new ArgumentOutOfRangeException(nameof(options), $"This device handler doesn't support any of the APIs allowed by the IQuerierOptions (allowed: {options.AllowedApis}, supported {this.SupportedApi}).");
            }

            LazyLoadingDeviceSystemData llsd = lowerLayer.SystemData as LazyLoadingDeviceSystemData;

            if (llsd != null)
            {
                if (oidLookup.TryGetValue(RetrievableValuesEnum.RxSignalStrengthCh0AppendMacAndInterfaceId, out DeviceSpecificOid oid0) && !oid0.Oid.IsNull)
                {
                    // for ALIX/H4L devices we currently only support RSSI querying
                    llsd.SupportedFeatures = DeviceSupportedFeatures.Rssi;
                }
            }
        }
Ejemplo n.º 21
0
 /// <summary>
 /// Constructs for the given lower layer.
 /// </summary>
 /// <param name="lowerLayer">The lower layer for talking to this device.</param>
 /// <param name="oidLookup">The OID lookup table for the device.</param>
 /// <param name="osVersion">The SW version of the device.</param>
 /// <param name="model">The device's model name. Shall be the same name as used for the device name during OID database lookups.</param>
 /// <param name="options">The options to use.</param>
 public GenericDeviceHandler(ISnmpLowerLayer lowerLayer, IDeviceSpecificOidLookup oidLookup, SemanticVersion osVersion, string model, IQuerierOptions options)
     : base(lowerLayer, oidLookup, osVersion, model, options)
 {
 }
Ejemplo n.º 22
0
        /// <inheritdoc />
        public override bool IsApplicableVendorSpecific(IpAddress address, IQuerierOptions options)
        {
            try
            {
                this.tikConnection = ConnectionFactory.CreateConnection(this.apiInUse);

                // following MUST be called before Open()
                this.tikConnection.SendTimeout    = Convert.ToInt32(options.Timeout.TotalMilliseconds);
                this.tikConnection.ReceiveTimeout = Convert.ToInt32(options.Timeout.TotalMilliseconds);

                this.tikConnection.Open(address.ToString(), options.LoginUser ?? string.Empty, options.LoginPassword ?? string.Empty);

                this.QuerySystemData();

                return(true);
            }
            catch (TikCommandException tikConnectionException)
            {
                this.CollectException("MTik APIv2", tikConnectionException);

                log.Info($"Device {address}: Mikrotik APIv2 connection failed: {tikConnectionException.Message}. Trying APIv1");

                if (this.tikConnection != null)
                {
                    this.tikConnection.Dispose();
                    this.tikConnection = null;
                }
            }
            catch (TikConnectionException tikConnectionException)
            {
                this.CollectException("MTik APIv2", tikConnectionException);

                log.Info($"Device {address}: Mikrotik APIv2 connection failed: {tikConnectionException.Message}. Trying APIv1");

                if (this.tikConnection != null)
                {
                    this.tikConnection.Dispose();
                    this.tikConnection = null;
                }
            }
            catch (IOException ioEx)
            {
                this.CollectException("MTik APIv2 (not falling back to v1 due to IOException being api-version-independent)", ioEx);

                log.Info($"Device {address}: I/O Exception in Mikrotik API connection: {ioEx.Message}. Considering device as not applicable");

                if (this.tikConnection != null)
                {
                    this.tikConnection.Dispose();
                    this.tikConnection = null;
                }

                return(false);
            }
            catch (SocketException socketEx)
            {
                this.CollectException("MTik APIv2 (not falling back to v1 due to SocketException being api-version-independent)", socketEx);

                log.Info($"Device {address}: Socket Exception in Mikrotik API connection: {socketEx.Message}. Considering device as not applicable");

                if (this.tikConnection != null)
                {
                    this.tikConnection.Dispose();
                    this.tikConnection = null;
                }

                return(false);
            }

            this.apiInUse = TikConnectionType.Api;
            try
            {
                this.tikConnection = ConnectionFactory.CreateConnection(this.apiInUse);

                // following MUST be called before Open()
                this.tikConnection.SendTimeout    = Convert.ToInt32(options.Timeout.TotalMilliseconds);
                this.tikConnection.ReceiveTimeout = Convert.ToInt32(options.Timeout.TotalMilliseconds);

                this.tikConnection.Open(address.ToString(), options.LoginUser ?? string.Empty, options.LoginPassword ?? string.Empty);

                this.QuerySystemData();

                return(true);
            }
            catch (TikCommandException tikConnectionException)
            {
                this.CollectException("MTik APIv1", tikConnectionException);

                log.Info($"Device {address}: Mikrotik APIv1 connection failed: {tikConnectionException.Message}. Assuming Mikrotik API NOT AVAILABLE");

                if (this.tikConnection != null)
                {
                    this.tikConnection.Dispose();
                    this.tikConnection = null;
                }

                return(false);
            }
            catch (TikConnectionException tikConnectionException)
            {
                this.CollectException("MTik APIv1", tikConnectionException);

                log.Info($"Device {address}: Mikrotik APIv1 connection failed: {tikConnectionException.Message}. Assuming Mikrotik API NOT AVAILABLE");

                if (this.tikConnection != null)
                {
                    this.tikConnection.Dispose();
                    this.tikConnection = null;
                }

                return(false);
            }
        }
Ejemplo n.º 23
0
 /// <inheritdoc />
 public override bool IsApplicableSnmp(ISnmpLowerLayer snmpLowerLayer, IQuerierOptions options)
 {
     // we're explicitly applicable to vendor-specific API only
     return(false);
 }
Ejemplo n.º 24
0
        /// <summary>
        /// Gets the device handler of the given handler class name via reflection.
        /// </summary>
        /// <param name="handlerClassName">The class name of the device handler to get.</param>
        /// <param name="lowerLayer">The lower layer for talking to this device.</param>
        /// <param name="oidTable">The OID lookup table for the device.</param>
        /// <param name="osVersion">The SW version of the device.</param>
        /// <param name="model">The device's model name. Shall be the same name as used for the device name during OID database lookups.</param>
        /// <param name="options">The options to use.</param>
        /// <returns>The generated device handler.</returns>
        protected IDeviceHandler GetHandlerViaReflection(string handlerClassName, ISnmpLowerLayer lowerLayer, IDeviceSpecificOidLookup oidTable, SemanticVersion osVersion, string model, IQuerierOptions options)
        {
            var type = Type.GetType($"SnmpAbstraction.{handlerClassName}");

            if (type == null)
            {
                var ex = new HamnetSnmpException($"{lowerLayer.Address} ({model} v {osVersion}): Cannot find a DeviceHandler implementation of name '{handlerClassName}'", lowerLayer.Address?.ToString());
                this.CollectException("Missing handler class name", ex);
                throw ex;
            }

            object myObject = null;

            try
            {
                myObject = Activator.CreateInstance(type, lowerLayer, oidTable, osVersion, model, options);
            }
            catch (Exception ex)
            {
                this.CollectException($"Instantiate handler class '{type.FullName}'", ex);

                throw new HamnetSnmpException($"{lowerLayer.Address} ({model} v {osVersion}): Exception while instantiating DeviceHandler of name '{handlerClassName}': {ex.Message}", ex, lowerLayer.Address?.ToString());
            }

            IDeviceHandler castedHandler = myObject as IDeviceHandler;

            if (castedHandler == null)
            {
                var ex = new HamnetSnmpException($"{lowerLayer.Address} ({model} v {osVersion}): Instantiating DeviceHandler of name '{handlerClassName}' is NOT an IDeviceHandler", lowerLayer.Address?.ToString());
                this.CollectException($"Cast handler class '{type.FullName}' to IDeviceHandler", ex);
                throw ex;
            }

            return(castedHandler);
        }
Ejemplo n.º 25
0
 /// <inheritdoc />
 public abstract bool IsApplicableVendorSpecific(IpAddress address, IQuerierOptions options);
Ejemplo n.º 26
0
 /// <inheritdoc />
 public abstract bool IsApplicableSnmp(ISnmpLowerLayer snmpLowerLayer, IQuerierOptions options);
Ejemplo n.º 27
0
        /// <summary>
        /// Trigger detection of the device based.<br/>
        /// Idea is, that this might already be sufficient to finally decide about a specific device and thus decision can be done without doing any network communiation.
        /// </summary>
        /// <returns>A <see cref="IDetectableDevice" /> representing the detected device or null if the device is unknown and cannot be detected.</returns>
        public IDeviceHandler Detect(IQuerierOptions options)
        {
            Stopwatch detectionDuration = Stopwatch.StartNew();

            var snmpVersionBackup = this.lowerLayer.ProtocolVersionInUse;

            // for device detection fall back to the lowest possible version
            this.lowerLayer.AdjustSnmpVersion(SnmpVersion.Ver1);

            List <IInfoAndException> collectedExceptions = new List <IInfoAndException>();

            var type = typeof(IDetectableDevice);
            var detectableDevices = Assembly.GetExecutingAssembly().GetTypes()
                                    .Where(p => type.IsAssignableFrom(p) && !p.IsAbstract && !p.IsInterface);

            var detectorList = detectableDevices
                               .Select(dt => (IDetectableDevice)Activator.CreateInstance(dt)) // instantiate the detectable devices that we've found
                               .Where(d => (d.SupportedApi & options.AllowedApis) != 0)       // choose only those which are allowed by selected API
                               .OrderByDescending(d => d.Priority);                           // and order by decreasing priority (so we take highest prio device first)

            IDetectableDevice deviceToUse = null;

            foreach (IDetectableDevice currentDevice in detectorList)
            {
                try
                {
                    if (options.AllowedApis.HasFlag(QueryApis.VendorSpecific) && currentDevice.IsApplicableVendorSpecific(this.lowerLayer.Address, options))
                    {
                        detectionDuration.Stop();

                        log.Debug($"Device detection of '{this.lowerLayer.Address}' took {detectionDuration.ElapsedMilliseconds} ms");

                        deviceToUse = currentDevice;
                    }
                    else if (options.AllowedApis.HasFlag(QueryApis.Snmp) && currentDevice.IsApplicableSnmp(this.lowerLayer, options))
                    {
                        detectionDuration.Stop();

                        log.Debug($"Device detection of '{this.lowerLayer.Address}' took {detectionDuration.ElapsedMilliseconds} ms");

                        deviceToUse = currentDevice;
                    }
                    else
                    {
                        collectedExceptions.AddRange(currentDevice.CollectedExceptions);
                        continue;
                    }
                }
                catch (SnmpException ex)
                {
                    collectedExceptions.Add(new InfoAndException {
                        Info = "Applicability check", Exception = ex
                    });

                    var errorInfo2 = $"SnmpException talking to device '{this.lowerLayer.Address}' during applicability check: {ex.Message}";

                    if (ex.Message.Equals("Request has reached maximum retries.") || ex.Message.ToLowerInvariant().Contains("timeout"))
                    {
                        var snmpErrorInfo = $"Timeout talking to device '{this.lowerLayer.Address}' during applicability check{Environment.NewLine}{Environment.NewLine}Collected Errors:{Environment.NewLine}{string.Join("\n", collectedExceptions.Select(e => e.ToString()))}";
                        log.Error(snmpErrorInfo, ex);

                        // Re-throwing a different exception is not good practice.
                        // But here we have a good reason: We need to add the IP address which timed out as that information is not contained in the SnmpException itself.
                        throw new HamnetSnmpException(snmpErrorInfo, ex, this.lowerLayer?.Address?.ToString());
                    }

                    log.Info($"Trying next device: {errorInfo2}");

                    continue;
                }
                catch (HamnetSnmpException ex)
                {
                    var errorInfo2 = $"HamnetSnmpException talking to device '{this.lowerLayer.Address}' during applicability check: {ex.Message}";

                    log.Info($"Trying next device: Exception talking to device '{this.lowerLayer.Address}' during applicability check", ex);

                    continue;
                }
                catch (Exception ex)
                {
                    collectedExceptions.Add(new InfoAndException {
                        Info = "Applicability check", Exception = ex
                    });

                    var errorInfo2 = $"Exception talking to device '{this.lowerLayer.Address}' during applicability check: {ex.Message}";

                    log.Info($"Trying next device: Exception talking to device '{this.lowerLayer.Address}' during applicability check", ex);

                    continue;
                }

                if ((currentDevice != null) && options.AllowedApis.HasFlag(QueryApis.VendorSpecific) && currentDevice.SupportedApi.HasFlag(QueryApis.VendorSpecific))
                {
                    // Vendor-specific is allowed and we found an applicable device that supports the vendor-specific API --> use it with top priority
                    break;
                }

                if ((currentDevice != null) && !options.AllowedApis.HasFlag(QueryApis.VendorSpecific) && currentDevice.SupportedApi.HasFlag(QueryApis.Snmp))
                {
                    // Vendor-specific is NOT allowed but we found an applicable device that supports SNMP --> use it as there's no chance to find a better one
                    break;
                }

                // continue searching for a better device (e.g. we found an SNMP device but a vendor-specific one would be preferred)
            }

            if (deviceToUse == null)
            {
                var snmpErrorInfo = $"Unsupported device at address '{this.lowerLayer.Address}': No applicable handler found (allowed APIs: {options.AllowedApis}){Environment.NewLine}Device System Data:{Environment.NewLine}{this.lowerLayer.SystemData}{Environment.NewLine}Collected Errors:{Environment.NewLine}{string.Join("\n", collectedExceptions.Select(e => e.ToString()))}";
                log.Error(snmpErrorInfo);

                // Re-throwing a different exception is not good practice.
                // But here we have a good reason: We need to add the IP address which timed out as that information is not contained in the SnmpException itself.
                throw new HamnetSnmpException(snmpErrorInfo, this.lowerLayer?.Address?.ToString());
            }

            try
            {
                var handler     = deviceToUse.CreateHandler(this.lowerLayer, options);
                var handlerBase = handler as DeviceHandlerBase;

                if (handler.SupportedApi.HasFlag(QueryApis.Snmp))
                {
                    var internalLowerLayer = this.lowerLayer as SnmpLowerLayer;
                    var internalSystemData = internalLowerLayer.InternalSystemData;
                    internalSystemData.ModifyableModel              = handler.Model;
                    internalSystemData.ModifyableVersion            = handler.OsVersion;
                    internalSystemData.ModifyableMaximumSnmpVersion = handlerBase.OidLookup.MaximumSupportedSnmpVersion;

                    if (handlerBase.OidLookup.MaximumSupportedSnmpVersion < snmpVersionBackup)
                    {
                        log.Info($"Device '{this.lowerLayer.Address}': Adjusting SNMP protocol version from {snmpVersionBackup} to {handlerBase.OidLookup.MaximumSupportedSnmpVersion} due to maximum version in device database");
                        internalLowerLayer.AdjustSnmpVersion(handlerBase.OidLookup.MaximumSupportedSnmpVersion);
                    }
                    else
                    {
                        this.lowerLayer.AdjustSnmpVersion(snmpVersionBackup);
                    }
                }

                return(handler);
            }
            catch (SnmpException ex)
            {
                collectedExceptions.Add(new InfoAndException {
                    Info = "Handler creation", Exception = ex
                });

                if (ex.Message.Equals("Request has reached maximum retries.") || ex.Message.ToLowerInvariant().Contains("timeout"))
                {
                    var snmpErrorInfo = $"Timeout talking to device '{this.lowerLayer.Address}' ({this.lowerLayer?.SystemData?.DeviceModel}) during handler creation{Environment.NewLine}Collected Errors:{Environment.NewLine}{string.Join("\n", collectedExceptions.Select(e => e.ToString()))}";
                    log.Error(snmpErrorInfo, ex);

                    // Re-throwing a different exception is not good practice.
                    // But here we have a good reason: We need to add the IP address which timed out as that information is not contained in the SnmpException itself.
                    throw new HamnetSnmpException(snmpErrorInfo, ex, this.lowerLayer?.Address?.ToString());
                }

                log.Error($"Trying next device: Exception talking to device '{this.lowerLayer.Address}' ({this.lowerLayer?.SystemData?.DeviceModel}) during handler creation: {ex.Message}");
            }
            catch (HamnetSnmpException ex)
            {
                log.Error($"Trying next device: Exception talking to device '{this.lowerLayer.Address}' ({this.lowerLayer?.SystemData?.DeviceModel}) during handler creation", ex);
            }

            detectionDuration.Stop();

            var errorInfo = $"Device '{this.lowerLayer.Address}' ({this.lowerLayer?.SystemData?.DeviceModel}) cannot be identified as a supported/known device after {detectionDuration.ElapsedMilliseconds} ms and trying {detectableDevices.Count()} devices.{Environment.NewLine}Collected Errors:{Environment.NewLine}{string.Join("\n", collectedExceptions.Select(e => e.ToString()))}";

            log.Error(errorInfo);
            throw new HamnetSnmpException(errorInfo, this.lowerLayer?.Address?.ToString());
        }
Ejemplo n.º 28
0
 /// <inheritdoc />
 public override bool IsApplicableVendorSpecific(IpAddress address, IQuerierOptions options)
 {
     // we only support SNMP
     return(false);
 }
Ejemplo n.º 29
0
        /// <summary>
        /// Constructs for the given lower layer.
        /// </summary>
        /// <param name="address">The address of this device.</param>
        /// <param name="connectionType">The type of the connection in use (required in case we have to re-open).</param>
        /// <param name="tikConnection">The lower layer connection for talking to this device.</param>
        /// <param name="options">The options to use.</param>
        /// <param name="sysIdent">The system ident (to create SystemData from).</param>
        /// /// <param name="sysResource">The system resource info (to create SystemData from).</param>
        /// <param name="sysRouterboard">The system routerboard info (to create SystemData from).</param>
        public MikrotikApiDeviceHandler(IpAddress address, TikConnectionType connectionType, ITikConnection tikConnection, IQuerierOptions options, SystemIdentity sysIdent, SystemResource sysResource, SystemRouterboard sysRouterboard)
        {
            if ((options.AllowedApis & this.SupportedApi) == 0)
            {
                throw new ArgumentOutOfRangeException(nameof(options), $"This device handler doesn't support any of the APIs allowed by the IQuerierOptions (allowed: {options.AllowedApis}, supported {this.SupportedApi}).");
            }

            this.sysRouterboard = sysRouterboard ?? throw new ArgumentNullException(nameof(sysRouterboard), "sysRouterboard is null when creating a MikrotikApiDeviceHandler");
            this.sysResource    = sysResource ?? throw new ArgumentNullException(nameof(sysResource), "sysResource is null when creating a MikrotikApiDeviceHandler");
            this.sysIdent       = sysIdent ?? throw new ArgumentNullException(nameof(sysIdent), "sysIdent is null when creating a MikrotikApiDeviceHandler");

            this.Address                = address ?? throw new ArgumentNullException(nameof(address), "address is null when creating a MikrotikApiDeviceHandler");
            this.TikConnection          = tikConnection ?? throw new ArgumentNullException(nameof(tikConnection), "tikConnection is null when creating a MikrotikApiDeviceHandler");
            this.DetectedConnectionType = connectionType;
            this.Options                = options ?? throw new ArgumentNullException(nameof(options), "options are null when creating a MikrotikApiDeviceHandler");
        }
Ejemplo n.º 30
0
 /// <summary>
 /// Initializes using the given lower layer.
 /// </summary>
 /// <param name="lowerLayer">The lower layer to use.</param>
 /// <param name="options">The options of the querier.</param>
 public CachingHamnetQuerier(ISnmpLowerLayer lowerLayer, IQuerierOptions options)
 {
     this.lowerLayer = lowerLayer ?? throw new ArgumentNullException(nameof(lowerLayer), "The lower layer SNMP engine is null");
     this.options    = options ?? throw new ArgumentNullException(nameof(options), "The querier options are null");
 }