broken
Harald Wolff 2019-03-13 08:20:53 +01:00
parent 834b2d9c48
commit f1f325a16a
11 changed files with 772 additions and 13 deletions

76
Check.cs 100644
View File

@ -0,0 +1,76 @@
// /**
// * File: Service.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
using System;
using ln.logging;
using System.Linq.Expressions;
using ln.perfdb;
namespace ln.skyscanner
{
public abstract class Check
{
public readonly String CheckID;
public DateTime LastCheck;
public DateTime NextCheck;
public TimeSpan CheckInterval;
public long LastCheckTime;
public String Name;
public Check()
{
CheckInterval = TimeSpan.FromSeconds(60);
}
public Check(String checkID)
{
CheckID = checkID;
CheckInterval = TimeSpan.FromSeconds(60);
}
public string GetPerfName(string perfName)
{
return string.Format("{0}_{1}",CheckID,perfName);
}
public void Mark()
{
NextCheck += CheckInterval;
if (NextCheck < DateTime.Now)
NextCheck = DateTime.Now + CheckInterval;
}
public void Run(IPerfFileProvider perfProvider)
{
long tStart = DateTimeOffset.Now.ToUnixTimeMilliseconds();
try
{
CheckImplementation(perfProvider);
LastCheck = DateTime.Now;
} catch (Exception e)
{
Logging.Log(LogLevel.WARNING, "Check {0} caught exception: {1}", CheckID, e);
Logging.Log(e);
}
LastCheckTime = DateTimeOffset.Now.ToUnixTimeMilliseconds() - tStart;
}
public abstract void CheckImplementation(IPerfFileProvider perfProvider);
public override int GetHashCode()
{
return CheckID.GetHashCode();
}
}
}

View File

@ -25,6 +25,10 @@ using System.Linq;
using ln.types;
using System.Runtime.InteropServices;
using ln.snmp.rfc1213;
using ln.types.sync;
using ln.skyscanner.crawl;
using System.Threading;
using System.Net.NetworkInformation;
namespace ln.skyscanner
{
@ -47,13 +51,31 @@ namespace ln.skyscanner
public static void Main(string[] args)
{
Logger.ConsoleLogger.MaxLogLevel = LogLevel.DEBUG;
//CIDR cidr = CIDR.Parse("255.255.255.255/28");
//Logging.Log(LogLevel.INFO, "{0} {1}",cidr,cidr.ToNetwork());
Crawler crawler = new Crawler();
Logging.Log(LogLevel.DEBUG, "Find host...");
//return;
crawler.Crawl(IPAddress.Parse("10.10.10.2"));
int n = 0;
do
{
Logging.Log(LogLevel.DEBUG, "QueuedJobs: {0} Pool: {1}", crawler.QueuedJobs, String.Join(", ", crawler.ThreadStates));
Thread.Sleep(1000);
if (crawler.QueuedJobs == 0)
n++;
else
n = 0;
} while (n < 10);
crawler.Stop();
crawler._CrawlPool.Save();
return;
//PerfFile perfFile = new PerfFile("test.lnpv");
//perfFile.Open();

72
check/Hostalive.cs 100644
View File

@ -0,0 +1,72 @@
// /**
// * File: Hostalive.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
using System;
using ln.types;
using ln.skyscanner.entities;
using ln.perfdb;
using System.Net.NetworkInformation;
using ln.logging;
using ln.perfdb.storage;
namespace ln.skyscanner.check
{
public class Hostalive : Check
{
public static String CalcCheckName(Node host)
{
return String.Format("hostalive-{0}", host.PrimaryIP.ToString());
}
public Node Host;
public Hostalive(Node node)
:base(CalcCheckName(node))
{
Host = node;
}
public override void CheckImplementation(IPerfFileProvider perfProvider)
{
Ping ping = new Ping();
long roundTripTime = 0;
int success = 0;
int n;
for (n = 0; n < 4; n++)
{
PingReply reply = ping.Send(Host.PrimaryIP, 250);
if (reply.Status == IPStatus.Success)
{
success++;
roundTripTime += reply.RoundtripTime;
}
}
float fSuccess = (float)success / (float)n;
PerfFile pfSuccess = perfProvider.GetPerfFile(GetPerfName("LOSS"));
pfSuccess.Write(new PerfValue(fSuccess));
if (success > 0)
{
roundTripTime /= success;
Logging.Log(LogLevel.INFO, "HOSTALIVE: IP {0} reachable ({1}/10) {2}ms", Host.PrimaryIP, success, roundTripTime);
PerfFile pfRoundTrip = perfProvider.GetPerfFile(GetPerfName("RTA"));
pfRoundTrip.Write(new PerfValue(roundTripTime));
}
else
{
Logging.Log(LogLevel.INFO, "HOSTALIVE: IP {0} unreachable", Host.PrimaryIP);
}
}
}
}

View File

@ -0,0 +1,77 @@
// /**
// * File: CrawledHost.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using ln.types;
using ln.types.sync;
using System.Net;
using System.IO;
using System.Net.NetworkInformation;
using ln.logging;
namespace ln.skyscanner.crawl
{
public class CrawledHost : Syncable
{
[Unsynced]
private Crawler crawler;
public Crawler Crawler { get => crawler; set => crawler = value; }
private Guid id;
public Guid ID { get => id; set => id = value; }
private List<CIDR> ipaddresses = new List<CIDR>();
public List<CIDR> IPAddresses => ipaddresses;
public CrawledHost()
{ }
public CrawledHost(Crawler crawler)
{
Crawler = crawler;
ID = Guid.NewGuid();
FileName = Path.Combine(crawler.BasePath, String.Format("{0}.ch", ID.ToString()));
}
public CrawledHost(Crawler crawler, Guid id)
:this(crawler)
{
ID = id;
FileName = Path.Combine(crawler.BasePath, String.Format("{0}.ch", ID.ToString()));
Load();
}
public CrawledHost(Crawler crawler, string filename)
: base(filename)
{
}
public void Crawl()
{
Logging.Log(LogLevel.INFO, "Checking {0}", id);
Ping ping = new Ping();
foreach (CIDR ip in IPAddresses)
{
PingReply pingReply = ping.Send(ip);
Logging.Log(LogLevel.INFO, "IP {0} Ping: {1}",ip,pingReply.RoundtripTime);
}
}
public override string ToString()
{
return String.Format("[CrawledHost ID={0} IPAddress=({1})]",ID,String.Join(", ",IPAddresses));
}
}
}

233
crawl/Crawler.cs 100644
View File

@ -0,0 +1,233 @@
// /**
// * File: Crawler.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
using System;
using ln.skyscanner.threads;
using System.Net;
using System.Collections.Generic;
using System.IO;
using ln.logging;
using ln.types;
using System.Linq;
using ln.types.serialize;
using ln.skyscanner.entities;
using System.Net.NetworkInformation;
using ln.snmp;
using ln.snmp.endpoint;
using ln.snmp.rfc1213;
using ln.perfdb;
using ln.perfdb.storage;
using ln.skyscanner.check;
using System.Threading;
namespace ln.skyscanner.crawl
{
public class Crawler : IPerfFileProvider
{
public String BasePath { get; set; }
public String PerfPath => Path.Combine(BasePath, "perfdb");
bool stopping;
Pool crawlThreadPool = new Pool();
public PoolThreadState[] ThreadStates => crawlThreadPool.ThreadStates;
public int QueuedJobs => crawlThreadPool.QueuedJobs;
public DiskObject<CrawlPool> _CrawlPool;
public CrawlPool CrawlPool => _CrawlPool.Instance;
public SNMPEngine SNMPEngine { get; }
Thread threadChecker;
public Crawler()
{
BasePath = Path.GetFullPath("./crawler");
if (!Directory.Exists(BasePath))
Directory.CreateDirectory(BasePath);
if (!Directory.Exists(PerfPath))
Directory.CreateDirectory(PerfPath);
SNMPEngine = new SNMPEngine();
_CrawlPool = new DiskObject<CrawlPool>(String.Format("{0}/pool",BasePath));
crawlThreadPool.NumThreads = 12;
threadChecker = new Thread(Checker);
threadChecker.Start();
}
public void Stop()
{
stopping = true;
crawlThreadPool.Close();
SNMPEngine.Close();
_CrawlPool.Save();
}
public void Enqueue(JobDelegate job)
{
crawlThreadPool.Enqueue(job);
}
public void Crawl(IPAddress host)
{
crawlThreadPool.Enqueue(() => crawlHost(host));
}
private void crawlHost(IPAddress host)
{
Ping ping = new Ping();
PingReply pingReply = ping.Send(host,500);
if (pingReply.Status != IPStatus.Success)
{
Logging.Log(LogLevel.INFO, "Host not reachable: {0} {1}", host, pingReply.Status);
}
else
{
//SnmpV1Endpoint v1endpoint = new SnmpV1Endpoint(engine, new IPEndPoint(IPAddress.Parse("10.75.1.10"), 161), "ByFR4oW98hap");
//SnmpV2Endpoint v2endpoint = new SnmpV2Endpoint(engine, new IPEndPoint(IPAddress.Parse("10.113.254.4"), 161), "ghE7wUmFPoPpkRno");
USMEndpoint v3endpoint = new USMEndpoint(SNMPEngine, new IPEndPoint(host, 161));
v3endpoint.AuthMethod = SnmpV3AuthMethod.SHA;
v3endpoint.AuthKeyPhrase = "qVy3hnZJ2fov";
v3endpoint.Username = "skytron";
try
{
RFC1213.Interface[] interfaces = RFC1213.GetInterfaces(v3endpoint);
foreach (RFC1213.Interface netIf in interfaces)
{
Logging.Log(LogLevel.INFO, "Interface: {0}", netIf);
foreach (CIDR ip in netIf.IPAddresses)
{
Subnet subnet = CrawlPool.GetSubnet(ip.Network);
if ((DateTime.Now - subnet.LastScanned).Hours >= 1)
{
Enqueue(() => crawlSubnet(ip.Network));
}
}
}
}
catch (TimeoutException)
{
Logging.Log(LogLevel.INFO, "Host: {0} SNMP communication timed out.", host);
}
}
}
public void crawlSubnet(CIDR subnet)
{
Ping ping = new Ping();
Subnet sub = CrawlPool.GetSubnet(subnet);
sub.LastScanned = DateTime.Now;
Logging.Log(LogLevel.INFO, "Scanning {0}", subnet);
foreach (CIDR ip in subnet)
{
long roundTripTime = 0;
int success = 0;
for (int n = 0; n < 4; n++)
{
PingReply reply = ping.Send(ip,250);
if (reply.Status == IPStatus.Success)
{
success++;
roundTripTime += reply.RoundtripTime;
}
}
if (success > 0)
{
roundTripTime /= success;
Logging.Log(LogLevel.INFO, "IP {0} reachable ({1}/10) {2}ms", ip, success, roundTripTime);
Node node = CrawlPool.GetNode(ip);
if ((DateTime.Now - node.LastSeen ).Hours > 0)
{
Enqueue(() => crawlHost(ip));
}
string checkID = Hostalive.CalcCheckName(node);
if (!CrawlPool.Checks.ContainsKey(checkID))
{
Hostalive hostalive = new Hostalive(node);
CrawlPool.Checks.Add(hostalive.CheckID, hostalive);
}
}
else
{
Logging.Log(LogLevel.INFO, "IP {0} unreachable", ip);
}
}
}
/** Checks **/
private void Checker()
{
while (!stopping)
{
DateTime now = DateTime.Now;
Check[] checks = CrawlPool.Checks.Values.ToArray();
if (checks.Length == 0)
Thread.Sleep(1000);
foreach (Check check in checks)
{
if (check.NextCheck <= now)
{
check.Mark();
Enqueue(() => check.Run(this));
}
}
}
}
/** PerfDB **/
Dictionary<string, PerfFile> perfFiles = new Dictionary<string, PerfFile>();
public PerfFile GetPerfFile(string name)
{
if (perfFiles.ContainsKey(name))
return perfFiles[name];
PerfFile perfFile = new PerfFile(Path.Combine(PerfPath, String.Format("{0}.perf", name)));
perfFile.Open();
perfFiles.Add(name, perfFile);
if (perfFile.FirstSection == null)
{
PerfFile.PerfFileSection section = new PerfFile.PerfFileSection(perfFile, null, 1440, 60, AggregationMethod.AVERAGE);
section = new PerfFile.PerfFileSection(perfFile, section, 1728, 300, AggregationMethod.AVERAGE);
section = new PerfFile.PerfFileSection(perfFile, section, 2016, 900, AggregationMethod.AVERAGE);
section = new PerfFile.PerfFileSection(perfFile, section, 1344, 3600, AggregationMethod.AVERAGE);
section = new PerfFile.PerfFileSection(perfFile, section, 1344, 10800, AggregationMethod.AVERAGE);
}
return perfFile;
}
}
}

View File

@ -0,0 +1,47 @@
// /**
// * File: CrawlPool.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
using System;
using System.Collections.Generic;
using ln.types;
namespace ln.skyscanner.entities
{
public class CrawlPool
{
public Dictionary<CIDR, Subnet> Subnets = new Dictionary<CIDR, Subnet>();
public Dictionary<CIDR, Node> Nodes = new Dictionary<CIDR, Node>();
public Dictionary<string, Check> Checks = new Dictionary<String, Check>();
public CrawlPool()
{
}
public Subnet GetSubnet(CIDR network)
{
if (Subnets.ContainsKey(network))
return Subnets[network];
Subnet subnet = new Subnet(network);
Subnets.Add(network,subnet);
return subnet;
}
public Node GetNode(CIDR ip)
{
if (Nodes.ContainsKey(ip))
return Nodes[ip];
Node node = new Node(ip);
Nodes[ip] = node;
return node;
}
}
}

View File

@ -8,23 +8,30 @@
// *
// **/
using System;
using ln.types;
using System.Collections.Generic;
namespace ln.skyscanner.entities
{
public class Node
{
public Guid ID { get; private set; }
public CIDR PrimaryIP;
public String Name;
public List<NetworkInterface> NetworkInterfaces = new List<NetworkInterface>();
public DateTime FirstSeen;
public DateTime LastSeen;
public Node()
{
ID = Guid.NewGuid();
FirstSeen = DateTime.Now;
}
public Node(Guid guid)
public Node(CIDR primaryIP)
{
ID = guid;
PrimaryIP = primaryIP;
FirstSeen = DateTime.Now;
}
}
}

View File

@ -1,5 +1,5 @@
// /**
// * File: NetworkInterface.cs
// * File: Router.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
@ -8,14 +8,29 @@
// *
// **/
using System;
using System.Collections.Generic;
using ln.types;
namespace ln.skyscanner.entities
{
public class NetworkInterface
{
public Node Node { get; private set; }
public Node Node;
public string Name;
public List<CIDR> IPs = new List<CIDR>();
public NetworkInterface()
{ }
public NetworkInterface(Node node)
{
Node = node;
}
public NetworkInterface(Node node,String name)
{
Node = node;
Name = name;
}
}
}

33
entities/Subnet.cs 100644
View File

@ -0,0 +1,33 @@
// /**
// * File: Subnet.cs
// * Author: haraldwolff
// *
// * This file and it's content is copyrighted by the Author and / or copyright holder.
// * Any use wihtout proper permission is illegal and may lead to legal actions.
// *
// *
// **/
using System;
using ln.types;
namespace ln.skyscanner.entities
{
public class Subnet
{
public readonly CIDR Network;
public String Name;
public DateTime FirstSeen;
public DateTime LastScanned;
public Subnet()
{
FirstSeen = DateTime.Now;
}
public Subnet(CIDR cidr)
{
Network = cidr;
FirstSeen = DateTime.Now;
}
}
}

View File

@ -40,7 +40,14 @@
<Compile Include="identify\NodeIdentifier.cs" />
<Compile Include="Target.cs" />
<Compile Include="entities\Node.cs" />
<Compile Include="entities\NetworkInterface.cs" />
<Compile Include="crawl\Crawler.cs" />
<Compile Include="threads\Pool.cs" />
<Compile Include="crawl\CrawledHost.cs" />
<Compile Include="entities\Subnet.cs" />
<Compile Include="entities\CrawlPool.cs" />
<Compile Include="entities\Router.cs" />
<Compile Include="Check.cs" />
<Compile Include="check\Hostalive.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
@ -49,6 +56,8 @@
<Folder Include="identify\" />
<Folder Include="check\" />
<Folder Include="entities\" />
<Folder Include="crawl\" />
<Folder Include="threads\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ln.snmp\ln.snmp.csproj">

168
threads/Pool.cs 100644
View File

@ -0,0 +1,168 @@
using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq.Expressions;
using ln.logging;
using System.Linq;
namespace ln.skyscanner.threads
{
public delegate void JobDelegate();
public enum PoolThreadState { READY, WORKING, EXITED }
public class Pool :IDisposable
{
public PoolThread[] PoolThreads => poolThreads.ToArray();
public int NumThreads
{
get => poolThreads.Count;
set => setPoolThreads(value);
}
public PoolThreadState[] ThreadStates => poolThreads.Select((x) => x.State).ToArray();
public int QueuedJobs => queuedJobs.Count;
private List<PoolThread> poolThreads = new List<PoolThread>();
private Queue<JobDelegate> queuedJobs = new Queue<JobDelegate>();
private bool stopping;
public Pool()
{
NumThreads = 4;
}
public void Close()
{
stopping = true;
NumThreads = 0;
stopping = false;
}
private void setPoolThreads(int count)
{
lock (poolThreads)
{
while (poolThreads.Count > count)
{
poolThreads[poolThreads.Count - 1].Exit();
}
while (poolThreads.Count < count)
{
poolThreads.Add(new PoolThread(this));
}
}
}
public void Enqueue(JobDelegate job)
{
if (stopping)
return;
lock (queuedJobs)
{
queuedJobs.Enqueue(job);
Monitor.Pulse(queuedJobs);
}
}
public class PoolThread
{
public Pool Pool { get; }
public Thread Thread { get; }
public PoolThreadState State { get; private set; }
private bool exitCalled { get; set; }
public PoolThread(Pool pool)
{
Pool = pool;
Thread = new Thread(thread);
WaitStarted();
}
private void WaitStarted()
{
lock (this)
{
Thread.Start();
Monitor.Wait(this, 5000);
}
}
private void thread()
{
lock (this)
{
Monitor.Pulse(this);
}
while (!exitCalled)
{
try
{
State = PoolThreadState.WORKING;
lock (Pool.queuedJobs)
{
while (!exitCalled && Pool.queuedJobs.Count > 0)
{
JobDelegate job = Pool.queuedJobs.Dequeue();
Monitor.Exit(Pool.queuedJobs);
Logging.Log(LogLevel.DEBUG, "PoolThread: starting next job {0}", job);
try
{
job();
}
catch (Exception ie)
{
Logging.Log(ie);
}
Monitor.Enter(Pool.queuedJobs);
State = PoolThreadState.READY;
Monitor.Wait(Pool.queuedJobs, 5000);
State = PoolThreadState.WORKING;
}
}
}
catch (Exception e)
{
Logging.Log(e);
}
}
lock (this)
{
State = PoolThreadState.EXITED;
Monitor.Pulse(this);
}
}
public void Exit()
{
lock (this)
{
exitCalled = true;
Monitor.Pulse(this);
Monitor.Wait(this);
Pool.poolThreads.Remove(this);
}
}
}
public void Dispose()
{
Close();
}
}
}