Beta for SNMP V1 & V2c

master
Harald Wolff 2019-03-04 12:06:45 +01:00
parent b8e9ba6e9a
commit 3c135de8db
17 changed files with 633 additions and 13 deletions

View File

@ -33,9 +33,21 @@ namespace ln.snmp
Number = number;
}
public byte Firstbyte
{
get
{
return (byte)(
((int)IdentifierClass << 6) |
(Constructed ? 0x20 : 0x00) |
(int)((Number < 31) ? Number : 0x1F)
);
}
}
public override string ToString()
{
return String.Format("[ASN.1 Type Class={0} Constructed={1} Number={2}]",IdentifierClass,Constructed,Number);
return String.Format("[ASN.1 Type Class={0} Constructed={1} Number={2} FirstByte=0x{3,02:X}]",IdentifierClass,Constructed,Number,Firstbyte);
}
}

View File

@ -9,24 +9,146 @@
// **/
using System;
using System.Collections.Generic;
using Lextm.SharpSnmpLib;
using ln.snmp.types;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace ln.snmp
{
public abstract class SNMPClient
public class SNMPClient : IDisposable
{
public SNMPClient()
{
public static bool DEBUG = false;
public UdpClient LocalEndpoint { get; private set; }
public int Timeout { get; set; } = 1000;
private Dictionary<IPEndPoint, InternalRequest> queuedRequests = new Dictionary<IPEndPoint, InternalRequest>();
private bool shutdown = false;
private Thread ReceiverThread { get; set; }
public SNMPClient(IPEndPoint localEndpoint)
{
LocalEndpoint = new UdpClient(localEndpoint);
ReceiverThread = new Thread(Receiver);
ReceiverThread.Start();
}
public abstract List<Variable> Walk(ObjectIdentifier baseOID);
public abstract List<Variable> Get(List<ObjectIdentifier> baseOID);
public virtual Variable Get(ObjectIdentifier oid)
public void Close()
{
return Get(new List<ObjectIdentifier>(new ObjectIdentifier[] { oid }))[0];
shutdown = true;
LocalEndpoint.Close();
}
private void Receiver()
{
while (!shutdown)
{
try
{
IPEndPoint remoteEndpoint = null;
byte[] datagram = LocalEndpoint.Receive(ref remoteEndpoint);
lock (queuedRequests)
{
if (queuedRequests.ContainsKey(remoteEndpoint))
{
InternalRequest internalRequest = queuedRequests[remoteEndpoint];
internalRequest.Response = datagram;
lock (internalRequest)
{
Monitor.PulseAll(internalRequest);
}
}
}
}
catch (SocketException se)
{
}
catch (Exception e)
{
Console.WriteLine("Receiver(): {0}", e);
}
}
}
private byte[] SendRequest(IPEndPoint remoteEndpoint,byte[] request,int timeout)
{
InternalRequest internalRequest = new InternalRequest();
internalRequest.RemoteEndpoint = remoteEndpoint;
lock(queuedRequests)
{
if (queuedRequests.ContainsKey(remoteEndpoint))
throw new ArgumentException("Already pending request exists for this remote endpoint", nameof(remoteEndpoint));
queuedRequests.Add(remoteEndpoint,internalRequest);
if (DEBUG)
Console.WriteLine("SNMPClient: Send: {0}", BitConverter.ToString(request));
LocalEndpoint.Send(request, request.Length, remoteEndpoint);
lock (internalRequest)
{
Monitor.Exit(queuedRequests);
bool success = Monitor.Wait(internalRequest, timeout);
Monitor.Enter(queuedRequests);
if (!success)
{
throw new TimeoutException();
}
}
queuedRequests.Remove(remoteEndpoint);
}
return internalRequest.Response;
}
public Variable SNMPRequest(IPEndPoint remoteEndpoint,Sequence request)
{
byte[] response = SendRequest(remoteEndpoint, request.ToBytes(), Timeout);
if (DEBUG)
Console.WriteLine("SNMPClient: Received: {0}", BitConverter.ToString(response));
Variable vreply = Variable.Read(response);
return vreply;
}
//public abstract List<Variable> Walk(ObjectIdentifier baseOID);
//public abstract List<Variable> Get(List<ObjectIdentifier> baseOID);
//public virtual Variable Get(ObjectIdentifier oid)
//{
// return Get(new List<ObjectIdentifier>(new ObjectIdentifier[] { oid }))[0];
//}
public void Dispose()
{
if (ReceiverThread != null)
{
if (ReceiverThread.IsAlive)
{
Close();
if (!ReceiverThread.Join(250))
{
ReceiverThread.Abort();
}
}
ReceiverThread = null;
}
}
class InternalRequest
{
public IPEndPoint RemoteEndpoint;
public byte[] Response;
}
}
}

142
SNMPInterface.cs 100644
View File

@ -0,0 +1,142 @@
using System;
using ln.snmp.types;
using System.Net;
using System.Collections.Generic;
using System.Linq;
using ln.snmp.channel;
namespace ln.snmp
{
public enum SnmpVersion : int { V1 = 0, V2c = 1, V3 = 3 }
public class SNMPInterface
{
public SnmpPDUChannel PDUChannel { get; set; }
public IPEndPoint RemoteEndpoint { get; set; }
public SnmpVersion SnmpVersion => PDUChannel.SnmpVersion;
public SNMPInterface(SnmpPDUChannel PDUChannel,IPEndPoint remoteEndpoint)
{
this.PDUChannel = PDUChannel;
RemoteEndpoint = remoteEndpoint;
}
private List<Sequence> snmpRequest<T>(IEnumerable<ObjectIdentifier> objectIdentifiers) where T: PDU, new()
{
T pdu = new T();
foreach (ObjectIdentifier oid in objectIdentifiers)
{
pdu.Add(new Sequence(new Variable[] { oid, NullValue.Instance }));
}
GetResponse responsePDU = PDUChannel.RequestResponse(pdu, RemoteEndpoint) as GetResponse;
Sequence varBinds = responsePDU.VarBinds as Sequence;
List<Sequence> results = new List<Sequence>();
foreach (Variable varBind in varBinds.Items)
{
results.Add(varBind as Sequence);
}
return results;
}
public List<Sequence> snmpGet(IEnumerable<ObjectIdentifier> objectIdentifiers)
{
return snmpRequest<GetRequest>(objectIdentifiers);
}
public Variable snmpGet(ObjectIdentifier objectIdentifier)
{
return snmpGet(new ObjectIdentifier[] { objectIdentifier })[0].Items[1];
}
public Variable snmpGet(string objectIdentifier)
{
return snmpGet(new ObjectIdentifier[] { new ObjectIdentifier(objectIdentifier) })[0].Items[1];
}
public List<Sequence> snmpGet(IEnumerable<String> objectIdentifiers)
{
return snmpGet(objectIdentifiers.Select((x) => new ObjectIdentifier(x)));
}
public List<Sequence> snmpGetNext(IEnumerable<ObjectIdentifier> objectIdentifiers)
{
return snmpRequest<GetNextRequest>(objectIdentifiers);
}
public Sequence snmpGetNext(ObjectIdentifier objectIdentifier)
{
return snmpGetNext(new ObjectIdentifier[] { objectIdentifier })[0];
}
public Sequence snmpGetNext(string objectIdentifier)
{
return snmpGetNext(new ObjectIdentifier[] { new ObjectIdentifier(objectIdentifier) })[0];
}
public List<Sequence> snmpGetNext(IEnumerable<String> objectIdentifiers)
{
return snmpGetNext(objectIdentifiers.Select((x) => new ObjectIdentifier(x)));
}
public List<Sequence> snmpGetBulk(ObjectIdentifier objectIdentifier)
{
List<Sequence> results = new List<Sequence>();
List<Sequence> parts;
do
{
parts = snmpRequest<GetBulkRequest>(new ObjectIdentifier[] { objectIdentifier });
foreach (Sequence ps in parts)
{
ObjectIdentifier oid = ps.Items[0] as ObjectIdentifier;
if (objectIdentifier.Contains(oid))
results.Add(ps);
else
return results;
}
} while (parts.Count >= 32);
return results;
}
public List<Sequence> snmpGetBulk(String objectIdentifier)
{
return snmpGetBulk(new ObjectIdentifier(objectIdentifier));
}
public List<Sequence> snmpWalk(ObjectIdentifier objectIdentifier)
{
if (SnmpVersion == SnmpVersion.V1)
{
List<Sequence> results = new List<Sequence>();
ObjectIdentifier oiLast = objectIdentifier;
while (objectIdentifier.Contains(oiLast))
{
Sequence next = snmpGetNext(oiLast);
oiLast = next.Items[0] as ObjectIdentifier;
if (objectIdentifier.Contains(oiLast))
results.Add(next);
}
return results;
} else if (SnmpVersion == SnmpVersion.V2c)
{
return snmpGetBulk(objectIdentifier);
}
else
{
throw new NotImplementedException();
}
}
public List<Sequence> snmpWalk(string objectIdentifier)
{
return snmpWalk(new ObjectIdentifier(objectIdentifier));
}
}
}

24
SnmpError.cs 100644
View File

@ -0,0 +1,24 @@
using System;
namespace ln.snmp
{
public class SnmpError : Exception
{
public override string Message { get; }
public int Error { get; }
public int ErrorIndex { get; }
public String ObjectIdentifier { get; }
public SnmpError(int error,int errorIndex,string objectIdentifier)
{
Error = error;
ErrorIndex = errorIndex;
ObjectIdentifier = objectIdentifier;
Message = ToString();
}
public override string ToString()
{
return String.Format("SNMP Error {0} (Index: {1} = {2}) was received",Error,ErrorIndex,ObjectIdentifier);
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using ln.snmp.types;
using System.Net;
namespace ln.snmp.channel
{
public abstract class SnmpPDUChannel
{
public SNMPClient SNMPClient { get; set; }
public abstract SnmpVersion SnmpVersion { get; }
public SnmpPDUChannel(SNMPClient client)
{
SNMPClient = client;
}
public abstract PDU RequestResponse(PDU pdu, IPEndPoint remoteEndpoint);
}
}

View File

@ -0,0 +1,48 @@
using System;
using ln.snmp.types;
using System.Net;
using System.Collections.Generic;
namespace ln.snmp.channel
{
public class SnmpV1Channel : SnmpPDUChannel
{
public override SnmpVersion SnmpVersion => SnmpVersion.V1;
public OctetString CommunityString { get; set; }
public SnmpV1Channel(SNMPClient client)
:base(client)
{
CommunityString = "public";
}
public SnmpV1Channel(SNMPClient client,string communityString)
:base(client)
{
CommunityString = communityString;
}
public override PDU RequestResponse(PDU pdu,IPEndPoint remoteEndpoint)
{
Integer version = new Integer((int)SnmpVersion);
Sequence snmpRequest = new Sequence();
snmpRequest.Add(version);
snmpRequest.Add(CommunityString);
snmpRequest.Add(pdu);
Variable reply = SNMPClient.SNMPRequest(remoteEndpoint, snmpRequest);
Sequence sreply = reply as Sequence;
PDU responsePDU = sreply.Items[2] as PDU;
if (responsePDU.Error.LongValue != 0)
{
throw new SnmpError(responsePDU.Error, responsePDU.ErrorIndex, ((pdu.VarBinds.Items[(int)responsePDU.ErrorIndex - 1] as Sequence).Items[0] as ObjectIdentifier).AsString);
}
return responsePDU;
}
}
}

View File

@ -0,0 +1,17 @@
using System;
namespace ln.snmp.channel
{
public class SnmpV2Channel : SnmpV1Channel
{
public override SnmpVersion SnmpVersion => SnmpVersion.V2c;
public SnmpV2Channel(SNMPClient client)
: base(client)
{
}
public SnmpV2Channel(SNMPClient client, string communityString)
: base(client, communityString)
{ }
}
}

View File

@ -45,12 +45,20 @@
<Compile Include="types\Boolean.cs" />
<Compile Include="types\OctetString.cs" />
<Compile Include="types\ObjectIdentifier.cs" />
<Compile Include="types\Unsigned32.cs" />
<Compile Include="types\Counter64.cs" />
<Compile Include="SNMPInterface.cs" />
<Compile Include="SnmpError.cs" />
<Compile Include="channel\SnmpPDUChannel.cs" />
<Compile Include="channel\SnmpV1Channel.cs" />
<Compile Include="channel\SnmpV2Channel.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Folder Include="types\" />
<Folder Include="channel\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

24
types/Counter64.cs 100644
View File

@ -0,0 +1,24 @@
using System;
namespace ln.snmp.types
{
public class Counter64 : Integer
{
public ulong ULongValue { get => (ulong)LongValue; set => LongValue = (long)Value; }
public Counter64()
: base(new Identifier(IdentifierClass.APPLICATION, false, 0x06))
{
}
public Counter64(uint value)
: this()
{
ULongValue = value;
}
public override string ToString()
{
return String.Format("[Counter64 ULongValue={0}]", ULongValue);
}
}
}

View File

@ -25,6 +25,11 @@ namespace ln.snmp.types
LongValue = value;
}
protected Integer(Identifier identifier)
:base(identifier)
{
}
public override byte[] Bytes
{
get => BasicEncodingRules.EncodeInteger(LongValue);
@ -32,5 +37,21 @@ namespace ln.snmp.types
}
public override object Value { get => LongValue; set => LongValue = (long)value; }
public override string ToString()
{
return String.Format("[Integer LongValue={0}]", LongValue);
}
public static implicit operator int(Integer integer)
{
return (int)integer.LongValue;
}
public static implicit operator long(Integer integer)
{
return integer.LongValue;
}
}
}

View File

@ -29,5 +29,11 @@ namespace ln.snmp.types
}
}
public override object Value { get => null; set => throw new NotImplementedException(); }
public override string ToString()
{
return "[Null]";
}
}
}

View File

@ -32,6 +32,23 @@ namespace ln.snmp.types
OIDValue = ioid;
}
/* Check is b is part of the my subtree or is equal*/
public bool Contains(ObjectIdentifier b)
{
int[] me = OIDValue;
int[] you = b.OIDValue;
if (me.Length > you.Length)
return false;
for (int n=0;n<me.Length;n++)
{
if (me[n] != you[n])
return false;
}
return true;
}
public override byte[] Bytes
{
@ -44,5 +61,15 @@ namespace ln.snmp.types
set => OIDValue = value as int[];
}
public string AsString
{
get => String.Join(".", OIDValue.Select((x) => x.ToString()));
}
public override string ToString()
{
return String.Format("[ObjectIdentifier OID={0}]", AsString);
}
}
}

View File

@ -34,5 +34,21 @@ namespace ln.snmp.types
get => StringValue;
set => StringValue = value as string;
}
public override string ToString()
{
return String.Format("[OctetString StringValue={0}]", StringValue);
}
public static implicit operator String(OctetString octetString)
{
return octetString.StringValue;
}
public static implicit operator OctetString(String text)
{
return new OctetString(text);
}
}
}

View File

@ -24,7 +24,7 @@ namespace ln.snmp.types
public PDU(Identifier identifier)
:base(identifier)
{
RequestID = new Integer(1); // Environment.TickCount
RequestID = new Integer(Environment.TickCount);
Error = new Integer();
ErrorIndex = new Integer();
@ -64,8 +64,40 @@ namespace ln.snmp.types
public class GetRequest : PDU
{
public GetRequest() : base(new Identifier(IdentifierClass.CONTEXT,true,0x00))
public GetRequest() : base(new Identifier(IdentifierClass.CONTEXT, true, 0x00))
{
}
}
public class GetNextRequest : PDU
{
public GetNextRequest() : base(new Identifier(IdentifierClass.CONTEXT, true, 0x01))
{
}
}
public class GetResponse : PDU
{
public GetResponse() : base(new Identifier(IdentifierClass.CONTEXT, true, 0x02))
{
}
}
public class GetBulkRequest: PDU
{
public Integer NonRepeaters => Error;
public Integer MaxRepetitions => ErrorIndex;
public GetBulkRequest() : base(new Identifier(IdentifierClass.CONTEXT, true, 0x05))
{
NonRepeaters.LongValue = 0;
MaxRepetitions.LongValue = 32;
}
}
public class SetRequest : PDU
{
public SetRequest() : base(new Identifier(IdentifierClass.CONTEXT, true, 0x03))
{
}
}
}

View File

@ -11,6 +11,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
namespace ln.snmp.types
{
public class Sequence : AbstractSequence
@ -93,5 +94,10 @@ namespace ln.snmp.types
}
}
public override string ToString()
{
return string.Format("[Sequence {0}]",String.Join(", ",Items.Select((x) => x.ToString())));
}
}
}

View File

@ -0,0 +1,64 @@
// /**
// * File: Integer.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;
namespace ln.snmp.types
{
public class Unsigned32 : Variable
{
public uint UIntValue { get; set; }
public Unsigned32()
:base(new Identifier(IdentifierClass.APPLICATION, false, 0x02))
{
}
public Unsigned32(uint value)
:this()
{
UIntValue = value;
}
protected Unsigned32(Identifier identifier)
: base(identifier)
{
}
protected Unsigned32(Identifier identifier,uint value)
: base(identifier)
{
UIntValue = value;
}
public override byte[] Bytes
{
get => BasicEncodingRules.EncodeInteger(UIntValue);
set => UIntValue = (uint)BasicEncodingRules.DecodeInteger(value);
}
public override object Value { get => UIntValue; set => UIntValue = (uint)value; }
public override string ToString()
{
return String.Format("[Unsigned32 UIntValue={0}]", UIntValue);
}
}
public class Counter32 : Unsigned32
{
public Counter32()
: base(new Identifier(IdentifierClass.APPLICATION, false, 0x01))
{
}
public Counter32(uint value)
: base(new Identifier(IdentifierClass.APPLICATION, false, 0x01),value)
{
}
}
}

View File

@ -32,6 +32,14 @@ namespace ln.snmp.types
stream.Write(payload, 0, payload.Length);
}
public byte[] ToBytes()
{
MemoryStream memoryStream = new MemoryStream();
Write(memoryStream);
return memoryStream.ToArray();
}
public static Variable Read(Stream stream)
{
Variable variable = null;
@ -47,6 +55,12 @@ namespace ln.snmp.types
return variable;
}
public static Variable Read(byte[] bytes)
{
MemoryStream stream = new MemoryStream(bytes);
return Read(stream);
}
public static void Write(Stream stream,Variable variable)
{
variable.Write(stream);
@ -70,12 +84,29 @@ namespace ln.snmp.types
case 0x10:
return new Sequence();
}
} else if (identifier.IdentifierClass == IdentifierClass.CONTEXT)
}
else if (identifier.IdentifierClass == IdentifierClass.CONTEXT)
{
switch (identifier.Number)
{
case 0x00:
return new GetRequest();
case 0x02:
return new GetResponse();
case 0x03:
return new SetRequest();
}
}
else if (identifier.IdentifierClass == IdentifierClass.APPLICATION)
{
switch (identifier.Number)
{
case 0x01:
return new Counter32();
case 0x02:
return new Unsigned32();
case 0x06:
return new Counter64();
}
}