SNMP Get
SNMP Get
Hi guys,
I'm looking for information on either a crossplatform dll(s) (Windows and Linux) with some code example (happy to purchase if required) or native code for SNMP Get (pref SNMP V2).
I should say that I have looked through all the topics on the forum for SNMP and found nothing up to date or any working code examples. Does anyone have and any information on SNMP with Purebasic?
I'm just looking to get basic traffic information off a router.
Many thanks for your help,
Ed
I'm looking for information on either a crossplatform dll(s) (Windows and Linux) with some code example (happy to purchase if required) or native code for SNMP Get (pref SNMP V2).
I should say that I have looked through all the topics on the forum for SNMP and found nothing up to date or any working code examples. Does anyone have and any information on SNMP with Purebasic?
I'm just looking to get basic traffic information off a router.
Many thanks for your help,
Ed
Re: SNMP Get
I cheated and used net-SNMP
net_SNMP is still being supported and seemed to work well last time I used it.
My current router doesn't do SNMP unfortunately so I don't have any use for it.
Jim
Code: Select all
; uses net-snmp
Output$ = ""
snmpstring.s="-v 2c -c public 192.168.88.1 1.3.6.1.2.1.1.3.0"
SNMPget = RunProgram("C:\apps\SNMP\bin\snmpget.exe", snmpstring, "", #PB_Program_Open | #PB_Program_Read | #PB_Program_Hide)
If SNMPget
While ProgramRunning(SNMPget)
If AvailableProgramOutput(SNMPget)
Output$ + ReadProgramString(SNMPget) + Chr(13)
EndIf
Wend
Output$ + Chr(13)
Output$ + "Exitcode: = " + Str(ProgramExitCode(SNMPget))
result = ProgramExitCode(SNMPget)
CloseProgram(SNMPget)
EndIf
If result = 0
dataIn$ = StringField(Output$,1,#CR$)
dataIn$ = StringField(StringField(dataIn$,2,"="),2,":")
dataOut$ = StringField(Output$,2,#CR$)
dataOut$ = StringField(StringField(dataOut$,2,"="),2,":")
upTime$ = StringField(Output$,3,#CR$)
upTime$ = StringField(upTime$,2,"=")
EndIf
Debug Output$
;MessageRequester("Output", Output$)
allData.s = "In "+dataIn$+#CRLF$+"Out "+dataOut$+#CRLF$+upTime$
MessageRequester("Data", allData)
;MessageRequester("In", dataIn$)
;MessageRequester("Out", dataOut$)
;MessageRequester("Time", upTime$)
;Debug Output$
My current router doesn't do SNMP unfortunately so I don't have any use for it.
Jim
Re: SNMP Get
Thanks Jim,
If all else fails that will be my option too
If all else fails that will be my option too
Re: SNMP Get
SNMP < V3 in PB is not really a problem.
But I'm on holiday, so I have not reall y access to my office PC.
In the mean time:
Have you tried this:
viewtopic.php?p=235852#p235852
But I'm on holiday, so I have not reall y access to my office PC.
In the mean time:
Have you tried this:
viewtopic.php?p=235852#p235852
Re: SNMP Get
Thanks for the link Infratec. Yes, I've tried that example and couldn't get it working.
Re: SNMP Get
Do you have snmpwalk running?
Then the example from Michael should also work.
Then the example from Michael should also work.
Re: SNMP Get
Yes Infratec, SNMP Walk working. I'll revisit Michael's code. All I was getting was 0's
Thanks again,
Ed
Thanks again,
Ed
Re: SNMP Get
An extended version of Michaels original:
This works for me.
Code: Select all
;
; snmp.pbi
;
; 1.20 use lists instead of fixed sized arrays
; 1.11 fix for unicode
; 1.10 added negative integers
; 1.01 fixed RequestID bug
; 1.00 initial release based on the code of Michael Vogel
; Define
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
CompilerEndIf
; Alive state
Enumeration
#Station_Unknown
#Station_Down
#Station_Up
EndEnumeration
; PDUType
Enumeration
#PDU_GET
#PDU_GET_NEXT
#PDU_RESPONSE
#PDU_SET
#PDU_TRAP
EndEnumeration
; Error
Enumeration
#ERRORSTATUS_NOERROR
#ERRORSTATUS_TOOBIG
#ERRORSTATUS_NOSUCHNAME
#ERRORSTATUS_BADVALUE
#ERRORSTATUS_READONLY
#ERRORSTATUS_GENERR
#ERRORSTATUS_NOACCESS
#ERRORSTATUS_WRONGTYPE
#ERRORSTATUS_WRONGLENGTH
#ERRORSTATUS_WRONGENCODING
#ERRORSTATUS_WRONGVALUE
#ERRORSTATUS_NOCREATION
#ERRORSTATUS_INCONSISTENTVALUE
#ERRORSTATUS_RESOURCEUNAVAILABLE
#ERRORSTATUS_COMMITFAILED
#ERRORSTATUS_UNDOFAILED
#ERRORSTATUS_AUTHORIZATIONERROR
#ERRORSTATUS_NOTWRITABLE
#ERRORSTATUS_INCONSISTENTNAME
EndEnumeration
; Status
Enumeration
#SNMP_Unknown = -1
#SNMP_Ok
#SNMP_Sent
#SNMP_Received
#SNMP_Error
#SNMP_Error_OID
#SNMP_Error_RID
#SNMP_Error_Name
EndEnumeration
#ClassUniversal = $00
#ClassApplication = $40
#ClassContextSpecific = $80
#ClassPrivate = $C0
#TypePrimitive = $00
#TypeConstructed = $20
;ValueType UniversalClass
#TypeUnknown = 0
#TypeBoolean = 1
#TypeInteger = 2
#TypeBitString = 3
#TypeOctetString = 4
#TypeNull = 5
#TypeObjectIdentifier = 6
#TypeSequence = 16
;ValueType ApplicationClass
#TypeNetworkAddress = 0
#TypeCounter = 1
#TypeGauge = 2
#TypeTimeTicks = 3
#TypeOpaque = 4
#TypeCounter64 = 6
#TypeUInt = 7
; internally used
Enumeration
#Field_Header
#Field_Version
#Field_Community
#Field_PDU
#Field_RequestID
#Field_ErrorState
#Field_ErrorIndex
#Field_VariableLengthWithHeader
#Field_VariableLengthWithoutHeader
#Field_OID
#Field_Variable
#Field_Nil
EndEnumeration
;#SNMP_MaxPacketSize = 548
#SNMP_MaxPacketSize = 1500
CompilerIf #PB_Compiler_OS = #PB_OS_Linux
#SOCKET_ERROR = -1
CompilerEndIf
Structure Reference
value.l
EndStructure
Structure StationType
IP.s
ReadCommunity.s
WriteCommunity.s
Alive.w
EndStructure
Structure SNMPType
StationNumber.w
PDUType.w
RequestID.l
OID.s
ResponseOID.s
ValueType.w
Value.s
Status.w
EndStructure
Global NewList SNMPStation.StationType()
Global NewList SNMP.SNMPType()
; EndDefine
Procedure.l BER_Length(*memory,*pos.Reference)
; Calculates length of actual field...
Protected.a byte
Protected.i length, n
byte = PeekA(*memory + *pos\Value)
*pos\Value + 1
If byte & $80; multi-byte length
byte & $3 ; number of bytes for length information
While byte
length << 8
length + (PeekA(*memory + *pos\Value))
byte - 1
*pos\Value + 1
Wend
Else
length = byte & $7f
EndIf
;Debug "Len: "+Str(length)
ProcedureReturn length
EndProcedure
Procedure.i BER_Integer(*memory, length)
; Encodes memory to integer value...
Protected.i n, Neg, i
If PeekA(*memory) & $80
Neg = #True
EndIf
i = length
While i
n << 8
n + (PeekA(*memory) )
i - 1
*memory + 1
Wend
If Neg
Select length
Case 1 : n = 0 - ($FF - n)
Case 2 : n = 0 - ($FFFF - n)
Case 3 : n = 0 - ($FFFFFF - n)
Case 4 : n = 0 - ($FFFFFFFF - n)
EndSelect
EndIf
ProcedureReturn n
EndProcedure
Procedure.s BER_ObjectIDentifier(*memory,length)
; Encodes Memory to OID string...
Protected.a byte
Protected.i ID, i
Protected OID$
While i < length
byte = PeekA(*memory + i)
If ID & $FE000000
OID$ + ".???"
Break
EndIf
ID << 7 ; ID -> helpbuffer if ID is > 128
ID | (byte & $7F) ; only the lower 7 bits are valid, 8th bit is indicator for 'more is following'
If byte & $80 = 0 ; if nothing follows
If i ; if not the first byte of the OID
OID$ + "." + Str(ID)
Else ; first byte is decoded different
OID$ = "." + Str(byte / 40) + "." + Str(byte % 40)
EndIf
ID = 0
EndIf
i + 1
Wend
ProcedureReturn OID$
EndProcedure
Procedure.l BM_Integer(value.q, *memory, *pos.Reference)
; Writes integer value into memory, starting at position *pos
; Returns number of used Bytes...
Protected.i IntSize, Bytes
Protected.q Mask
IntSize = 4
Mask = $FF800000
While ((value & Mask = 0) Or (value & Mask = Mask)) And IntSize > 1
value << 8
IntSize - 1
Wend
Mask = $FF000000
Bytes = 0
While IntSize <> 0
PokeA(*memory + *pos\value + Bytes, (value & Mask) >> 24)
value << 8
IntSize - 1
Bytes + 1
Wend
*pos\value + Bytes
ProcedureReturn Bytes
EndProcedure
Procedure.l BM_Value(value.l, *memory, *pos. Reference)
; should use quads, but because of pures poor quad handling limited to long for now ;(
; Writes integer value into memory, starting at position *pos
; Returns number of used Bytes...
If value >= 0 And value < 128
PokeA(*memory + *pos\Value, value)
value = 1
ElseIf value >= 128 And value < 16384
; Debug Str(value)+" = "+Str($80|(value>>7))+", "+Str(value&$7f)
PokeA(*memory+ *pos\Value, $80 | (value >> 7))
PokeA(*memory+ *pos\Value + 1, value & $7f)
value = 2
ElseIf value >= 16384 And value < 2097152
PokeA(*memory + *pos\Value, $80 | ((value >> 14) & $7f))
PokeA(*memory + *pos\Value + 1, $80 | ((value >> 7) & $7f))
PokeA(*memory + *pos\Value + 2, value & $7f)
value = 3
ElseIf value >= 2097152 And value < 268435456
PokeA(*memory + *pos\Value, $80 | ((value >> 21) & $7f))
PokeA(*memory + *pos\Value + 1, $80 | ((value >> 14) & $7f))
PokeA(*memory + *pos\Value + 2, $80 | ((value >> 7) & $7f))
PokeA(*memory + *pos\Value + 3, value & $7f)
value = 4
ElseIf value >= 268435456 And value < 4294967296
PokeA(*memory + *pos\Value, $80 | ((value >> 28) & $7f))
PokeA(*memory + *pos\Value + 1, $80 | ((value >> 21) & $7f))
PokeA(*memory + *pos\Value + 2, $80 | ((value >> 14) & $7f))
PokeA(*memory + *pos\Value + 3, $80 | ((value >> 7) & $7f))
PokeA(*memory + *pos\Value + 4, value & $7f)
value = 5
Else
value = 0
EndIf
*pos\Value + value
ProcedureReturn value
EndProcedure
Procedure.i BM_String(value$, *memory, *pos. Reference)
; Writes string value into memory, starting at position *pos
; Returns number of used Bytes...
Protected length = Len(value$)
If length
PokeS(*memory + *pos\Value, value$, length, #PB_Ascii)
*pos\Value + length
EndIf
ProcedureReturn length
EndProcedure
Procedure.i BM_ObjectIDentifier(OID.s, *memory, *pos. Reference)
; Writes OID object into memory starting at position *pos
; Returns number of used bytes
Protected value
Protected i, n
Protected length = 0
OID = LTrim(OID, ".")
n = CountString(OID, ".")
value = Val(StringField(OID, 1, ".")) * 40 + Val(StringField(OID, 2, "."))
length + BM_Value(value, *memory, *pos)
If CountString(OID, ".") > 1
For i = 3 To n + 1
length + BM_Value(Val(StringField(OID, i, ".")), *memory, *pos)
Next
EndIf
ProcedureReturn length
EndProcedure
Procedure.l BM_Move(value.l, length.l, *memory, *pos. Reference)
; Insert byte value into memory block...
; Returns total length
MoveMemory(*memory + *pos\Value - length, *memory + *pos\Value - length + 2, length)
PokeA(*memory + *pos\Value - length, value)
PokeA(*memory + *pos\Value - length + 1, length)
*pos\Value + 2
ProcedureReturn length + 2
EndProcedure
Procedure.l BM_Null(value.l, *memory, *pos. Reference)
; Writes byte value into memory address...
; Returns length (=2)
PokeA(*memory + *pos\Value, value)
PokeA(*memory + *pos\Value + 1, 0)
*pos\Value + 2
ProcedureReturn 2
EndProcedure
CompilerIf #PB_Compiler_OS <> #PB_OS_Linux
Procedure.l Ping(n)
; Pings Station(n)\IP...
; Returns #True if a reply packet is seen from the station
Protected EchoMessage.s
Protected EchoSize.l
Protected *Echo.ICMP_ECHO_REPLY
Protected *EchoResult
Protected Handle
SelectElement(SNMPStation(), n)
With SNMPStation()
\Alive = #Station_Down
EchoMessage.s = "Ping"
EchoSize = SizeOf(ICMP_ECHO_REPLY) + Len(EchoMessage)
*EchoResult = AllocateMemory(EchoSize)
*Echo = *EchoResult
If \IP
Handle = IcmpCreateFile_()
If IcmpSendEcho_(Handle, \IP,EchoMessage, Len(EchoMessage), 0, *EchoResult, EchoSize, 500)
; Received an ICMP-Response...
; If PeekL(*EchoResult)=\IP
;...from the destination address (otherwise it will be a destination unreachable message from the default gateway)
\Alive = #Station_Up
; EndIf
EndIf
IcmpCloseHandle_(Handle)
EndIf
FreeMemory(*EchoResult)
ProcedureReturn \Alive
EndWith
EndProcedure
CompilerEndIf
Procedure.l PDU_Check(n, *memory)
Protected.a byte
Protected.i length, pos, field
Protected Value$
; Class (Bits 7/8)
; 0 - Universal (integer, string etc.)
; 1 - Application (IP address, Time Ticks)
; 2 - Context (complex data)
; 3 - Private (non standard data)
; Data Type (Bit 6)
; 0 - primitive data-type
; 1 - constructed data-type
; ASN.1 Type (Bit 5)
; 0 - no
; 1 - yes
SelectElement(SNMP(), n)
With SNMP()
pos = 0
Repeat
byte = PeekA(*memory + pos)
;Debug "Field: "+Str(field)+" Pos: "+Str(pos)+" (=$"+Hex(byte&$ff)+")"
pos + 1
length = BER_Length(*memory, @pos);
Select byte & $C0 ; mask the class...
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Case #ClassUniversal ; universal class
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If byte & #TypeConstructed ; constructed data type
; Debug "Constructed"
If byte & $10 ; ASN.1
;Debug " Length: "+Str(length)
EndIf
Else; primitive data type
Select byte & $1F
Case #TypeInteger
\ValueType = #TypeInteger
Value$ = Str(BER_Integer(*memory + pos, length))
Debug " integer: " + Value$
Case #TypeBitString
Debug " bit string"
Case #TypeOctetString
\ValueType = #TypeOctetString
Value$ = PeekS(*memory+pos, length, #PB_Ascii)
Debug " octet string: " + Value$
Case #TypeNull
Debug " null"
Case #TypeObjectIdentifier
\ValueType = #TypeObjectIdentifier
Value$ = BER_ObjectIDentifier(*memory + pos, length)
Debug " object: " + Value$
Default
Debug Str(n) + " UNKNOWN TYPE: " + Str(byte)
EndSelect
pos + length
EndIf
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Case #ClassApplication ; application
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If byte & #TypeConstructed = 0; primitive data type
Select byte & $1F
Case #TypeNetworkAddress; IPAddress
Value$ = ""
Protected i
For i = 0 To length - 1
Value$ + Str(PeekA(*memory + pos + i) )
If i < length - 1 : Value$ + "." : EndIf
Next i
\ValueType = #TypeNetworkAddress
Case #TypeCounter; Counter
Value$ = Str(BER_Integer(*memory + pos, length))
\ValueType = #TypeCounter
Case #TypeGauge; Gauge
Value$ = Str(BER_Integer(*memory + pos, length))
\ValueType = #TypeGauge
Case #TypeTimeTicks; ticks
Value$ = Str(BER_Integer(*memory + pos, length))
\ValueType = #TypeTimeTicks
Case #TypeOpaque; Opaque
Value$ = PeekS(*memory + pos, length)
\ValueType = #TypeOpaque
EndSelect
EndIf
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Case #ClassContextSpecific ; context specific
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If byte & #TypeConstructed; constructed data type
Select byte & $1F
Case #PDU_RESPONSE
Debug "GetResponse PDU..."
Case #PDU_TRAP
Debug "Trap PDU..."
EndSelect
Else; primitive data type
Debug Str(n) + " ERROR: Unknown Packet"
\Status = #SNMP_Error
field = #Field_Nil
EndIf
EndSelect
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Select field
Case #Field_ErrorState
If Val(Value$)
\Status = #SNMP_Error_Name
Debug \Status
Debug Str(n) + " NAME ERROR: " + Value$
field=#Field_Nil
EndIf
Case #Field_RequestID
If Val(Value$) <> \RequestID
\Status = #SNMP_Error_RID
Debug Str(n) + " RID ERROR: " + Value$ + "~" + \RequestID
field = #Field_Nil
EndIf
Case #Field_OID
\ResponseOID = Value$
If \PDUType = #PDU_GET And Value$ <> \OID
\Status = #SNMP_Error_OID
Debug Str(n) + " OID ERROR: " + Value$ + "~" + \OID
field = #Field_Nil
EndIf
Case #Field_Variable
\Value = Value$
\Status = #SNMP_Ok
EndSelect
field + 1
Until field > #Field_Variable
ProcedureReturn \Status
EndWith
EndProcedure
Procedure.l PDU_Make(n, *memory)
; compose SNMP packet into reserved memory (548 bytes)
; n = Request number
; Returns packet length
Protected length = 0
Protected pos = 0
Protected PositionA, PositionB
Static RequestID.l = 0
SelectElement(SNMP(), n)
With SNMP()
SelectElement(SNMPStation(), \StationNumber)
RequestID + 1;Random(65535)
If RequestID = 65536 : RequestID = 0 : EndIf ; toavoid trouble wit negative values
\RequestID = RequestID
; If \GetType <> #GET_NEXT : \GetType = #GET : EndIf
; hier muss später die Gesamtlänge rein
; SNMP Version (V1 = 0)
length = BM_Integer($00, *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
; Community
If \PDUType = #PDU_GET Or \PDUType = #PDU_GET_NEXT
length = BM_String(SNMPStation()\ReadCommunity, *memory, @pos)
Else
length = BM_String(SNMPStation()\WriteCommunity, *memory, @pos)
EndIf
length = BM_Move(#TypeOctetString, length, *memory, @pos)
PositionA = pos ; hier muss später der Requesttype und seine Länge rein
; Request ID
length = BM_Integer(\RequestID, *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
; Error-Status
length = BM_Value(#ERRORSTATUS_NOERROR, *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
; Error-Index
length = BM_Value(0, *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
PositionB = pos ; hier muss später die Varbindlistlänge rein
; hier muss später die Länge des ersten varbinds rein
; OID
length = BM_ObjectIDentifier(\OID, *memory, @pos)
\OID = BER_ObjectIDentifier(*memory + PositionB, length); put normalized OID into database
length = BM_Move(#TypeObjectIdentifier, length, *memory, @pos)
If \PDUType = #PDU_GET Or \PDUType = #PDU_GET_NEXT
; NULL
length = BM_Null(#TypeNull, *memory, @pos)
Else
; put the value for a set request
Select \ValueType
Case #TypeInteger
length = BM_Integer(Val(\Value), *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
Case #TypeOctetString
length = BM_String(\Value, *memory, @pos)
length = BM_Move(#TypeOctetString, length, *memory, @pos)
EndSelect
EndIf
; Envelope OID and Null-field (two times)
length = BM_Move(#ClassUniversal|#TypeConstructed|#TypeSequence, pos - PositionB, *memory, @pos) ; trage Länge Varbind ein
length = BM_Move(#ClassUniversal|#TypeConstructed|#TypeSequence, pos - PositionB, *memory, @pos) ; trage Länge Varbindlist ein
; Envelope Request-ID, Error Information and OID-Envelope
length = BM_Move(#ClassContextSpecific|#TypeConstructed|\PDUType, pos - PositionA, *memory, @pos) ; trage Typ und Länge der PDU ein
; Envelope all SNMP fields
length = BM_Move(#ClassUniversal|#TypeConstructed|#TypeSequence, pos, *memory, @pos) ; trage Gesamtlänge ein
EndWith
ProcedureReturn pos
EndProcedure
Procedure.l SNMPRequest(n)
Protected *Buffer
Protected.i Status, Start, Client
SelectElement(SNMP(), n)
With SNMP()
SelectElement(SNMPStation(), \StationNumber)
\Status = #SNMP_Unknown
Status = 0
If SNMPStation()\IP
Client = OpenNetworkConnection(SNMPStation()\IP, 161 , #PB_Network_UDP)
If Client
*Buffer = AllocateMemory(#SNMP_MaxPacketSize)
If *Buffer
Status = PDU_Make(n, *Buffer)
If Status
If SendNetworkData(Client, *Buffer, Status) = Status
Start = Date()
Repeat
Delay(1)
Status = NetworkClientEvent(Client)
If Date() - Start > 5
Debug "Timeout"
Status = #SOCKET_ERROR ; = -1 Timeout
EndIf
Until Status <> 0
If Status = #PB_NetworkEvent_Data ; 2
If ReceiveNetworkData(Client, *Buffer, #SNMP_MaxPacketSize) > 0
\Status = PDU_Check(n, *Buffer)
If \Status = #SNMP_Ok : Status = 1
Else : Status = 0 : EndIf
Else
Status = 0
\Status = -99 ; ReceiveLengthError
EndIf
Else
Status = 0
\Status = -98 ; ReceiveError #SNMP_Error
EndIf
EndIf
EndIf
FreeMemory(*Buffer)
EndIf
CloseNetworkConnection(Client)
EndIf
EndIf
EndWith
ProcedureReturn Status
EndProcedure
CompilerIf #PB_Compiler_IsMainFile
If InitNetwork()
AddElement(SNMPStation())
SNMPStation()\IP = "192.168.0.1"
SNMPStation()\ReadCommunity = "public"
SNMPStation()\WriteCommunity = "private"
AddElement(SNMP())
SNMP()\StationNumber = 0
SNMP()\OID ="1.3.6.1.4.1.1909.32.3.1.1.3.4"
SNMP()\PDUType = #PDU_GET
SNMP()\Value = ""
SNMP()\ValueType = #TypeUnknown
SNMP()\Status = #SNMP_Unknown
SNMPRequest(0)
If SNMP()\Status = #SNMP_Error
MessageRequester("Results", "Error")
Else
MessageRequester("Results", "Status: " + Str(SNMP()\Status) + #LF$ + "Type: " + Str(SNMP()\ValueType) + #LF$ + "Value: " + SNMP()\Value)
EndIf
EndIf
CompilerEndIf
Re: SNMP Get
Thank you Infratec, that is perfect.
Thanks again for your help,
Ed
Thanks again for your help,
Ed
Re: SNMP Get
Sorry, me again.
Infratec's (Michael's) code works perfectly for SNMP V1 but not V2 so I cant view any 64bit counters.
Anyone have anything that works for V2?
Many thanks,
Ed
Infratec's (Michael's) code works perfectly for SNMP V1 but not V2 so I cant view any 64bit counters.
Anyone have anything that works for V2?
Many thanks,
Ed
Re: SNMP Get
https://www.dpstele.com/snmp/v1-v2c-v3-difference.phpThe Get, GetNext, and Set operations used in SNMPv1 are identical as those used in SNMPv2c.
Did you use WireShark to see what happens?
Or do you really need v2 and not v2c ?
Personally I only know v2c devices.
Re: SNMP Get
Hi Infratec,
I have tried v2c connection to the router and it works perfectly with SnmpGet and also using PHP snmp2_get. I haven't tried Wireshark to inspect what is going on, but with the Pure Basic code I'm getting 'NAME ERROR' returned for any OID I try that is a 64bit counter - 32bit counters work perfectly. The same problem occurs using MIB Browser if I set it to V1 instead of V2. Unfortunately, I'm looking at retrieving port TX data 64-bit Octet counter .
Is your code example for V2C ? And I'm missing something ?
Many thanks for your help
I have tried v2c connection to the router and it works perfectly with SnmpGet and also using PHP snmp2_get. I haven't tried Wireshark to inspect what is going on, but with the Pure Basic code I'm getting 'NAME ERROR' returned for any OID I try that is a 64bit counter - 32bit counters work perfectly. The same problem occurs using MIB Browser if I set it to V1 instead of V2. Unfortunately, I'm looking at retrieving port TX data 64-bit Octet counter .
Is your code example for V2C ? And I'm missing something ?
Many thanks for your help
Re: SNMP Get
You can try this:
Code: Select all
;
; snmp.pbi
;
; 1.22 added SNMP version and Counter64
; 1.21 extended BER_Integer()
; 1.20 use lists instead of fixed sized arrays
; 1.11 fix for unicode
; 1.10 added negative integers
; 1.01 fixed RequestID bug
; 1.00 initial release based on the code of Michael Vogel
; Define
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
CompilerEndIf
; Alive state
Enumeration
#Station_Unknown
#Station_Down
#Station_Up
EndEnumeration
; SNMPVersion
Enumeration
#SNMPv1
#SNMPv2c
EndEnumeration
; PDUType
Enumeration
#PDU_GET
#PDU_GET_NEXT
#PDU_RESPONSE
#PDU_SET
#PDU_TRAP
EndEnumeration
; Error
Enumeration
#ERRORSTATUS_NOERROR
#ERRORSTATUS_TOOBIG
#ERRORSTATUS_NOSUCHNAME
#ERRORSTATUS_BADVALUE
#ERRORSTATUS_READONLY
#ERRORSTATUS_GENERR
#ERRORSTATUS_NOACCESS
#ERRORSTATUS_WRONGTYPE
#ERRORSTATUS_WRONGLENGTH
#ERRORSTATUS_WRONGENCODING
#ERRORSTATUS_WRONGVALUE
#ERRORSTATUS_NOCREATION
#ERRORSTATUS_INCONSISTENTVALUE
#ERRORSTATUS_RESOURCEUNAVAILABLE
#ERRORSTATUS_COMMITFAILED
#ERRORSTATUS_UNDOFAILED
#ERRORSTATUS_AUTHORIZATIONERROR
#ERRORSTATUS_NOTWRITABLE
#ERRORSTATUS_INCONSISTENTNAME
EndEnumeration
; Status
Enumeration
#SNMP_Unknown = -1
#SNMP_Ok
#SNMP_Sent
#SNMP_Received
#SNMP_Error
#SNMP_Error_OID
#SNMP_Error_RID
#SNMP_Error_Name
EndEnumeration
#ClassUniversal = $00
#ClassApplication = $40
#ClassContextSpecific = $80
#ClassPrivate = $C0
#TypePrimitive = $00
#TypeConstructed = $20
;ValueType UniversalClass
#TypeUnknown = 0
#TypeBoolean = 1
#TypeInteger = 2
#TypeBitString = 3
#TypeOctetString = 4
#TypeNull = 5
#TypeObjectIdentifier = 6
#TypeSequence = 16
;ValueType ApplicationClass
#TypeNetworkAddress = 0
#TypeCounter = 1
#TypeGauge = 2
#TypeTimeTicks = 3
#TypeOpaque = 4
#TypeCounter64 = 6
#TypeUInt = 7
; internally used
Enumeration
#Field_Header
#Field_Version
#Field_Community
#Field_PDU
#Field_RequestID
#Field_ErrorState
#Field_ErrorIndex
#Field_VariableLengthWithHeader
#Field_VariableLengthWithoutHeader
#Field_OID
#Field_Variable
#Field_Nil
EndEnumeration
;#SNMP_MaxPacketSize = 548
#SNMP_MaxPacketSize = 1500
CompilerIf #PB_Compiler_OS = #PB_OS_Linux
#SOCKET_ERROR = -1
CompilerEndIf
Structure Reference
value.l
EndStructure
Structure StationType
IP.s
ReadCommunity.s
WriteCommunity.s
SNMPVersion.i
Alive.w
EndStructure
Structure SNMPType
StationNumber.w
PDUType.w
RequestID.l
OID.s
ResponseOID.s
ValueType.w
Value.s
Status.w
EndStructure
Global NewList SNMPStation.StationType()
Global NewList SNMP.SNMPType()
; EndDefine
Procedure.i BER_Length(*memory, *pos.Reference)
; Calculates length of actual field...
Protected byte.a, length.l, n.i
byte = PeekA(*memory + *pos\Value)
*pos\Value + 1
If byte & $80; multi-byte length
byte & $3 ; number of bytes for length information
While byte
length << 8
length + (PeekA(*memory + *pos\Value))
byte - 1
*pos\Value + 1
Wend
Else
length = byte & $7f
EndIf
;Debug "Len: "+Str(length)
ProcedureReturn length
EndProcedure
Procedure.q BER_Integer(*memory, length.i)
; Encodes memory to integer value...
Protected n.q, Neg.i, i.i
If PeekA(*memory) & $80
Neg = #True
EndIf
i = length
While i
n << 8
n | PeekA(*memory)
i - 1
*memory + 1
Wend
If Neg
Select length
Case 1 : n = 0 - ($FF - n)
Case 2 : n = 0 - ($FFFF - n)
Case 3 : n = 0 - ($FFFFFF - n)
Case 4 : n = 0 - ($FFFFFFFF - n)
Case 5 : n = 0 - ($FFFFFFFFFF - n)
Case 6 : n = 0 - ($FFFFFFFFFFFF - n)
Case 7 : n = 0 - ($FFFFFFFFFFFFFF - n)
Case 8 : n = 0 - ($FFFFFFFFFFFFFFFF - n)
EndSelect
EndIf
ProcedureReturn n
EndProcedure
Procedure.s BER_ObjectIDentifier(*memory, length.i)
; Encodes Memory to OID string...
Protected byte.a, ID.i, i.i, OID$
While i < length
byte = PeekA(*memory + i)
If ID & $FE000000
OID$ + ".???"
Break
EndIf
ID << 7 ; ID -> helpbuffer if ID is > 128
ID | (byte & $7F) ; only the lower 7 bits are valid, 8th bit is indicator for 'more is following'
If byte & $80 = 0 ; if nothing follows
If i ; if not the first byte of the OID
OID$ + "." + Str(ID)
Else ; first byte is decoded different
OID$ = "." + Str(byte / 40) + "." + Str(byte % 40)
EndIf
ID = 0
EndIf
i + 1
Wend
ProcedureReturn OID$
EndProcedure
Procedure.l BM_Integer(value.q, *memory, *pos.Reference)
; Writes integer value into memory, starting at position *pos
; Returns number of used Bytes...
Protected IntSize.i, Bytes.i, Mask.q
IntSize = 4
Mask = $FF800000
While ((value & Mask = 0) Or (value & Mask = Mask)) And IntSize > 1
value << 8
IntSize - 1
Wend
Mask = $FF000000
Bytes = 0
While IntSize <> 0
PokeA(*memory + *pos\value + Bytes, (value & Mask) >> 24)
value << 8
IntSize - 1
Bytes + 1
Wend
*pos\value + Bytes
ProcedureReturn Bytes
EndProcedure
Procedure.i BM_Value(value.i, *memory, *pos.Reference)
; should use quads, but because of pures poor quad handling limited to long for now ;(
; Writes integer value into memory, starting at position *pos
; Returns number of used Bytes...
If value >= 0 And value < 128
PokeA(*memory + *pos\Value, value)
value = 1
ElseIf value >= 128 And value < 16384
; Debug Str(value)+" = "+Str($80|(value>>7))+", "+Str(value&$7f)
PokeA(*memory+ *pos\Value, $80 | (value >> 7))
PokeA(*memory+ *pos\Value + 1, value & $7f)
value = 2
ElseIf value >= 16384 And value < 2097152
PokeA(*memory + *pos\Value, $80 | ((value >> 14) & $7f))
PokeA(*memory + *pos\Value + 1, $80 | ((value >> 7) & $7f))
PokeA(*memory + *pos\Value + 2, value & $7f)
value = 3
ElseIf value >= 2097152 And value < 268435456
PokeA(*memory + *pos\Value, $80 | ((value >> 21) & $7f))
PokeA(*memory + *pos\Value + 1, $80 | ((value >> 14) & $7f))
PokeA(*memory + *pos\Value + 2, $80 | ((value >> 7) & $7f))
PokeA(*memory + *pos\Value + 3, value & $7f)
value = 4
ElseIf value >= 268435456 And value < 4294967296
PokeA(*memory + *pos\Value, $80 | ((value >> 28) & $7f))
PokeA(*memory + *pos\Value + 1, $80 | ((value >> 21) & $7f))
PokeA(*memory + *pos\Value + 2, $80 | ((value >> 14) & $7f))
PokeA(*memory + *pos\Value + 3, $80 | ((value >> 7) & $7f))
PokeA(*memory + *pos\Value + 4, value & $7f)
value = 5
Else
value = 0
EndIf
*pos\Value + value
ProcedureReturn value
EndProcedure
Procedure.i BM_String(value$, *memory, *pos.Reference)
; Writes string value into memory, starting at position *pos
; Returns number of used Bytes...
Protected length.i
length = Len(value$)
If length
PokeS(*memory + *pos\Value, value$, length, #PB_Ascii)
*pos\Value + length
EndIf
ProcedureReturn length
EndProcedure
Procedure.i BM_ObjectIDentifier(OID.s, *memory, *pos.Reference)
; Writes OID object into memory starting at position *pos
; Returns number of used bytes
Protected value.i, i.i, n.i, length.i
OID = LTrim(OID, ".")
n = CountString(OID, ".")
value = Val(StringField(OID, 1, ".")) * 40 + Val(StringField(OID, 2, "."))
length + BM_Value(value, *memory, *pos)
If CountString(OID, ".") > 1
For i = 3 To n + 1
length + BM_Value(Val(StringField(OID, i, ".")), *memory, *pos)
Next
EndIf
ProcedureReturn length
EndProcedure
Procedure.i BM_Move(value.i, length.i, *memory, *pos.Reference)
; Insert byte value into memory block...
; Returns total length
MoveMemory(*memory + *pos\Value - length, *memory + *pos\Value - length + 2, length)
PokeA(*memory + *pos\Value - length, value)
PokeA(*memory + *pos\Value - length + 1, length)
*pos\Value + 2
ProcedureReturn length + 2
EndProcedure
Procedure.i BM_Null(value.i, *memory, *pos.Reference)
; Writes byte value into memory address...
; Returns length (=2)
PokeA(*memory + *pos\Value, value)
PokeA(*memory + *pos\Value + 1, 0)
*pos\Value + 2
ProcedureReturn 2
EndProcedure
CompilerIf #PB_Compiler_OS <> #PB_OS_Linux
Procedure.l Ping(n)
; Pings Station(n)\IP...
; Returns #True if a reply packet is seen from the station
Protected EchoMessage.s
Protected EchoSize.l
Protected *Echo.ICMP_ECHO_REPLY
Protected *EchoResult
Protected Handle
SelectElement(SNMPStation(), n)
With SNMPStation()
\Alive = #Station_Down
EchoMessage.s = "Ping"
EchoSize = SizeOf(ICMP_ECHO_REPLY) + Len(EchoMessage)
*EchoResult = AllocateMemory(EchoSize)
*Echo = *EchoResult
If \IP
Handle = IcmpCreateFile_()
If IcmpSendEcho_(Handle, \IP,EchoMessage, Len(EchoMessage), 0, *EchoResult, EchoSize, 500)
; Received an ICMP-Response...
; If PeekL(*EchoResult)=\IP
;...from the destination address (otherwise it will be a destination unreachable message from the default gateway)
\Alive = #Station_Up
; EndIf
EndIf
IcmpCloseHandle_(Handle)
EndIf
FreeMemory(*EchoResult)
ProcedureReturn \Alive
EndWith
EndProcedure
CompilerEndIf
Procedure.l PDU_Check(n, *memory)
Protected.a byte
Protected.i length, pos, field
Protected Value$
; Class (Bits 7/8)
; 0 - Universal (integer, string etc.)
; 1 - Application (IP address, Time Ticks)
; 2 - Context (complex data)
; 3 - Private (non standard data)
; Data Type (Bit 6)
; 0 - primitive data-type
; 1 - constructed data-type
; ASN.1 Type (Bit 5)
; 0 - no
; 1 - yes
SelectElement(SNMP(), n)
With SNMP()
pos = 0
Repeat
byte = PeekA(*memory + pos)
;Debug "Field: "+Str(field)+" Pos: "+Str(pos)+" (=$"+Hex(byte&$ff)+")"
pos + 1
length = BER_Length(*memory, @pos);
Select byte & $C0 ; mask the class...
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Case #ClassUniversal ; universal class
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If byte & #TypeConstructed ; constructed data type
; Debug "Constructed"
If byte & $10 ; ASN.1
;Debug " Length: "+Str(length)
EndIf
Else; primitive data type
Select byte & $1F
Case #TypeInteger
\ValueType = #TypeInteger
Value$ = Str(BER_Integer(*memory + pos, length))
Debug " integer: " + Value$
Case #TypeBitString
Debug " bit string"
Case #TypeOctetString
\ValueType = #TypeOctetString
Value$ = PeekS(*memory+pos, length, #PB_Ascii)
Debug " octet string: " + Value$
Case #TypeNull
Debug " null"
Case #TypeObjectIdentifier
\ValueType = #TypeObjectIdentifier
Value$ = BER_ObjectIDentifier(*memory + pos, length)
Debug " object: " + Value$
Default
Debug Str(n) + " UNKNOWN TYPE: " + Str(byte)
EndSelect
pos + length
EndIf
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Case #ClassApplication ; application
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If byte & #TypeConstructed = 0; primitive data type
Select byte & $1F
Case #TypeNetworkAddress; IPAddress
Value$ = ""
Protected i
For i = 0 To length - 1
Value$ + Str(PeekA(*memory + pos + i) )
If i < length - 1 : Value$ + "." : EndIf
Next i
\ValueType = #TypeNetworkAddress
Case #TypeCounter; Counter
Value$ = Str(BER_Integer(*memory + pos, length))
\ValueType = #TypeCounter
Case #TypeGauge; Gauge
Value$ = Str(BER_Integer(*memory + pos, length))
\ValueType = #TypeGauge
Case #TypeTimeTicks; ticks
Value$ = Str(BER_Integer(*memory + pos, length))
\ValueType = #TypeTimeTicks
Case #TypeOpaque; Opaque
Value$ = PeekS(*memory + pos, length)
\ValueType = #TypeOpaque
; Added by Hujambo
Case #TypeCounter64; Counter 64 bit
Value$ = Str(BER_Integer(*memory + pos, length))
\ValueType = #TypeCounter64
EndSelect
EndIf
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Case #ClassContextSpecific ; context specific
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If byte & #TypeConstructed; constructed data type
Select byte & $1F
Case #PDU_RESPONSE
Debug "GetResponse PDU..."
Case #PDU_TRAP
Debug "Trap PDU..."
EndSelect
Else; primitive data type
Debug Str(n) + " ERROR: Unknown Packet"
\Status = #SNMP_Error
field = #Field_Nil
EndIf
EndSelect
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Select field
Case #Field_ErrorState
If Val(Value$)
\Status = #SNMP_Error_Name
Debug \Status
Debug Str(n) + " NAME ERROR: " + Value$
field=#Field_Nil
EndIf
Case #Field_RequestID
If Val(Value$) <> \RequestID
\Status = #SNMP_Error_RID
Debug Str(n) + " RID ERROR: " + Value$ + "~" + \RequestID
field = #Field_Nil
EndIf
Case #Field_OID
\ResponseOID = Value$
If \PDUType = #PDU_GET And Value$ <> \OID
\Status = #SNMP_Error_OID
Debug Str(n) + " OID ERROR: " + Value$ + "~" + \OID
field = #Field_Nil
EndIf
Case #Field_Variable
\Value = Value$
\Status = #SNMP_Ok
EndSelect
field + 1
Until field > #Field_Variable
ProcedureReturn \Status
EndWith
EndProcedure
Procedure.l PDU_Make(n, *memory)
; compose SNMP packet into reserved memory (548 bytes)
; n = Request number
; Returns packet length
Protected length = 0
Protected pos = 0
Protected PositionA, PositionB
Static RequestID.l = 0
SelectElement(SNMP(), n)
With SNMP()
SelectElement(SNMPStation(), \StationNumber)
RequestID + 1
If RequestID = 65536 : RequestID = 0 : EndIf ; toavoid trouble wit negative values
\RequestID = RequestID
; If \GetType <> #GET_NEXT : \GetType = #GET : EndIf
; hier muss später die Gesamtlänge rein
length = BM_Integer(SNMPStation()\SNMPVersion, *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
; Community
If \PDUType = #PDU_GET Or \PDUType = #PDU_GET_NEXT
length = BM_String(SNMPStation()\ReadCommunity, *memory, @pos)
Else
length = BM_String(SNMPStation()\WriteCommunity, *memory, @pos)
EndIf
length = BM_Move(#TypeOctetString, length, *memory, @pos)
PositionA = pos ; hier muss später der Requesttype und seine Länge rein
; Request ID
length = BM_Integer(\RequestID, *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
; Error-Status
length = BM_Value(#ERRORSTATUS_NOERROR, *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
; Error-Index
length = BM_Value(0, *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
PositionB = pos ; hier muss später die Varbindlistlänge rein
; hier muss später die Länge des ersten varbinds rein
; OID
length = BM_ObjectIDentifier(\OID, *memory, @pos)
\OID = BER_ObjectIDentifier(*memory + PositionB, length); put normalized OID into database
length = BM_Move(#TypeObjectIdentifier, length, *memory, @pos)
If \PDUType = #PDU_GET Or \PDUType = #PDU_GET_NEXT
; NULL
length = BM_Null(#TypeNull, *memory, @pos)
Else
; put the value for a set request
Select \ValueType
Case #TypeInteger
length = BM_Integer(Val(\Value), *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
Case #TypeOctetString
length = BM_String(\Value, *memory, @pos)
length = BM_Move(#TypeOctetString, length, *memory, @pos)
EndSelect
EndIf
; Envelope OID and Null-field (two times)
length = BM_Move(#ClassUniversal|#TypeConstructed|#TypeSequence, pos - PositionB, *memory, @pos) ; trage Länge Varbind ein
length = BM_Move(#ClassUniversal|#TypeConstructed|#TypeSequence, pos - PositionB, *memory, @pos) ; trage Länge Varbindlist ein
; Envelope Request-ID, Error Information and OID-Envelope
length = BM_Move(#ClassContextSpecific|#TypeConstructed|\PDUType, pos - PositionA, *memory, @pos) ; trage Typ und Länge der PDU ein
; Envelope all SNMP fields
length = BM_Move(#ClassUniversal|#TypeConstructed|#TypeSequence, pos, *memory, @pos) ; trage Gesamtlänge ein
EndWith
ProcedureReturn pos
EndProcedure
Procedure.i SNMPRequest(n.i)
Protected *Buffer, Status.i, Start.i, Client.i
SelectElement(SNMP(), n)
With SNMP()
SelectElement(SNMPStation(), \StationNumber)
\Status = #SNMP_Unknown
Status = 0
If SNMPStation()\IP
Client = OpenNetworkConnection(SNMPStation()\IP, 161 , #PB_Network_UDP)
If Client
*Buffer = AllocateMemory(#SNMP_MaxPacketSize)
If *Buffer
Status = PDU_Make(n, *Buffer)
If Status
If SendNetworkData(Client, *Buffer, Status) = Status
Start = Date()
Repeat
Delay(1)
Status = NetworkClientEvent(Client)
If Date() - Start > 5
Debug "Timeout"
Status = #SOCKET_ERROR ; = -1 Timeout
EndIf
Until Status <> 0
If Status = #PB_NetworkEvent_Data ; 2
If ReceiveNetworkData(Client, *Buffer, #SNMP_MaxPacketSize) > 0
\Status = PDU_Check(n, *Buffer)
If \Status = #SNMP_Ok : Status = 1
Else : Status = 0 : EndIf
Else
Status = 0
\Status = -99 ; ReceiveLengthError
EndIf
Else
Status = 0
\Status = -98 ; ReceiveError #SNMP_Error
EndIf
EndIf
EndIf
FreeMemory(*Buffer)
EndIf
CloseNetworkConnection(Client)
EndIf
EndIf
EndWith
ProcedureReturn Status
EndProcedure
CompilerIf #PB_Compiler_IsMainFile
If InitNetwork()
AddElement(SNMPStation())
SNMPStation()\IP = "192.168.0.1"
SNMPStation()\ReadCommunity = "public"
SNMPStation()\WriteCommunity = "private"
SNMPStation()\SNMPVersion = #SNMPv1
AddElement(SNMP())
SNMP()\StationNumber = 0
SNMP()\OID ="1.3.6.1.4.1.1909.32.3.1.1.3.4"
SNMP()\PDUType = #PDU_GET
SNMP()\Value = ""
SNMP()\ValueType = #TypeUnknown
SNMP()\Status = #SNMP_Unknown
SNMPRequest(0)
If SNMP()\Status = #SNMP_Error
MessageRequester("Results", "Error")
Else
MessageRequester("Results", "Status: " + Str(SNMP()\Status) + #LF$ + "Type: " + Str(SNMP()\ValueType) + #LF$ + "Value: " + SNMP()\Value)
EndIf
EndIf
CompilerEndIf
Last edited by infratec on Thu Sep 30, 2021 9:14 am, edited 4 times in total.
Re: SNMP Get
Thanks Infratec, I've just adjusted your/Michaels code for 2.1c and it seems to work. Will try this latest option too.
Code: Select all
;
; snmp.pbi
;
; Adjusted by Hujambo for v2.1c and 64 bit, thanks to Infratec
; 1.20 use lists instead of fixed sized arrays
; 1.11 fix for unicode
; 1.10 added negative integers
; 1.01 fixed RequestID bug
; 1.00 initial release based on the code of Michael Vogel
; Define
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
CompilerEndIf
; Alive state
Enumeration
#Station_Unknown
#Station_Down
#Station_Up
EndEnumeration
; PDUType
Enumeration
#PDU_GET
#PDU_GET_NEXT
#PDU_RESPONSE
#PDU_SET
#PDU_TRAP
EndEnumeration
; Error
Enumeration
#ERRORSTATUS_NOERROR
#ERRORSTATUS_TOOBIG
#ERRORSTATUS_NOSUCHNAME
#ERRORSTATUS_BADVALUE
#ERRORSTATUS_READONLY
#ERRORSTATUS_GENERR
#ERRORSTATUS_NOACCESS
#ERRORSTATUS_WRONGTYPE
#ERRORSTATUS_WRONGLENGTH
#ERRORSTATUS_WRONGENCODING
#ERRORSTATUS_WRONGVALUE
#ERRORSTATUS_NOCREATION
#ERRORSTATUS_INCONSISTENTVALUE
#ERRORSTATUS_RESOURCEUNAVAILABLE
#ERRORSTATUS_COMMITFAILED
#ERRORSTATUS_UNDOFAILED
#ERRORSTATUS_AUTHORIZATIONERROR
#ERRORSTATUS_NOTWRITABLE
#ERRORSTATUS_INCONSISTENTNAME
EndEnumeration
; Status
Enumeration
#SNMP_Unknown = -1
#SNMP_Ok
#SNMP_Sent
#SNMP_Received
#SNMP_Error
#SNMP_Error_OID
#SNMP_Error_RID
#SNMP_Error_Name
EndEnumeration
#ClassUniversal = $00
#ClassApplication = $40
#ClassContextSpecific = $80
#ClassPrivate = $C0
#TypePrimitive = $00
#TypeConstructed = $20
;ValueType UniversalClass
#TypeUnknown = 0
#TypeBoolean = 1
#TypeInteger = 2
#TypeBitString = 3
#TypeOctetString = 4
#TypeNull = 5
#TypeObjectIdentifier = 6
#TypeSequence = 16
;ValueType ApplicationClass
#TypeNetworkAddress = 0
#TypeCounter = 1
#TypeGauge = 2
#TypeTimeTicks = 3
#TypeOpaque = 4
#TypeCounter64 = 6
#TypeUInt = 7
; internally used
Enumeration
#Field_Header
#Field_Version
#Field_Community
#Field_PDU
#Field_RequestID
#Field_ErrorState
#Field_ErrorIndex
#Field_VariableLengthWithHeader
#Field_VariableLengthWithoutHeader
#Field_OID
#Field_Variable
#Field_Nil
EndEnumeration
;#SNMP_MaxPacketSize = 548
#SNMP_MaxPacketSize = 1500
CompilerIf #PB_Compiler_OS = #PB_OS_Linux
#SOCKET_ERROR = -1
CompilerEndIf
Structure Reference
value.l
EndStructure
Structure StationType
IP.s
ReadCommunity.s
WriteCommunity.s
Alive.w
EndStructure
Structure SNMPType
StationNumber.w
PDUType.w
RequestID.l
OID.s
ResponseOID.s
ValueType.w
Value.s
Status.w
EndStructure
Global NewList SNMPStation.StationType()
Global NewList SNMP.SNMPType()
; EndDefine
Procedure.l BER_Length(*memory,*pos.Reference)
; Calculates length of actual field...
Protected.a byte
Protected.i length, n
byte = PeekA(*memory + *pos\Value)
*pos\Value + 1
If byte & $80; multi-byte length
byte & $3 ; number of bytes for length information
While byte
length << 8
length + (PeekA(*memory + *pos\Value))
byte - 1
*pos\Value + 1
Wend
Else
length = byte & $7f
EndIf
;Debug "Len: "+Str(length)
ProcedureReturn length
EndProcedure
Procedure.i BER_Integer(*memory, length)
; Encodes memory to integer value...
Protected.i n, Neg, i
If PeekA(*memory) & $80
Neg = #True
EndIf
i = length
While i
n << 8
n + (PeekA(*memory) )
i - 1
*memory + 1
Wend
If Neg
Select length
Case 1 : n = 0 - ($FF - n)
Case 2 : n = 0 - ($FFFF - n)
Case 3 : n = 0 - ($FFFFFF - n)
Case 4 : n = 0 - ($FFFFFFFF - n)
EndSelect
EndIf
ProcedureReturn n
EndProcedure
Procedure.s BER_ObjectIDentifier(*memory,length)
; Encodes Memory to OID string...
Protected.a byte
Protected.i ID, i
Protected OID$
While i < length
byte = PeekA(*memory + i)
If ID & $FE000000
OID$ + ".???"
Break
EndIf
ID << 7 ; ID -> helpbuffer if ID is > 128
ID | (byte & $7F) ; only the lower 7 bits are valid, 8th bit is indicator for 'more is following'
If byte & $80 = 0 ; if nothing follows
If i ; if not the first byte of the OID
OID$ + "." + Str(ID)
Else ; first byte is decoded different
OID$ = "." + Str(byte / 40) + "." + Str(byte % 40)
EndIf
ID = 0
EndIf
i + 1
Wend
ProcedureReturn OID$
EndProcedure
Procedure.l BM_Integer(value.q, *memory, *pos.Reference) ; Changed by Hujambo to Quad
; Writes integer value into memory, starting at position *pos
; Returns number of used Bytes...
Protected.i IntSize, Bytes
Protected.q Mask
IntSize = 4
Mask = $FF800000
While ((value & Mask = 0) Or (value & Mask = Mask)) And IntSize > 1
value << 8
IntSize - 1
Wend
Mask = $FF000000
Bytes = 0
While IntSize <> 0
PokeA(*memory + *pos\value + Bytes, (value & Mask) >> 24)
value << 8
IntSize - 1
Bytes + 1
Wend
*pos\value + Bytes
ProcedureReturn Bytes
EndProcedure
Procedure.q BM_Value(value.q, *memory, *pos. Reference)
; should use quads, but because of pures poor quad handling limited to long for now ;(
; Writes integer value into memory, starting at position *pos
; Returns number of used Bytes...
If value >= 0 And value < 128
PokeA(*memory + *pos\Value, value)
value = 1
ElseIf value >= 128 And value < 16384
; Debug Str(value)+" = "+Str($80|(value>>7))+", "+Str(value&$7f)
PokeA(*memory+ *pos\Value, $80 | (value >> 7))
PokeA(*memory+ *pos\Value + 1, value & $7f)
value = 2
ElseIf value >= 16384 And value < 2097152
PokeA(*memory + *pos\Value, $80 | ((value >> 14) & $7f))
PokeA(*memory + *pos\Value + 1, $80 | ((value >> 7) & $7f))
PokeA(*memory + *pos\Value + 2, value & $7f)
value = 3
ElseIf value >= 2097152 And value < 268435456
PokeA(*memory + *pos\Value, $80 | ((value >> 21) & $7f))
PokeA(*memory + *pos\Value + 1, $80 | ((value >> 14) & $7f))
PokeA(*memory + *pos\Value + 2, $80 | ((value >> 7) & $7f))
PokeA(*memory + *pos\Value + 3, value & $7f)
value = 4
ElseIf value >= 268435456 And value < 4294967296
PokeA(*memory + *pos\Value, $80 | ((value >> 28) & $7f))
PokeA(*memory + *pos\Value + 1, $80 | ((value >> 21) & $7f))
PokeA(*memory + *pos\Value + 2, $80 | ((value >> 14) & $7f))
PokeA(*memory + *pos\Value + 3, $80 | ((value >> 7) & $7f))
PokeA(*memory + *pos\Value + 4, value & $7f)
value = 5
Else
value = 0
EndIf
*pos\Value + value
ProcedureReturn value
EndProcedure
Procedure.i BM_String(value$, *memory, *pos. Reference)
; Writes string value into memory, starting at position *pos
; Returns number of used Bytes...
Protected length = Len(value$)
If length
PokeS(*memory + *pos\Value, value$, length, #PB_Ascii)
*pos\Value + length
EndIf
ProcedureReturn length
EndProcedure
Procedure.i BM_ObjectIDentifier(OID.s, *memory, *pos. Reference)
; Writes OID object into memory starting at position *pos
; Returns number of used bytes
Protected value
Protected i, n
Protected length = 0
OID = LTrim(OID, ".")
n = CountString(OID, ".")
value = Val(StringField(OID, 1, ".")) * 40 + Val(StringField(OID, 2, "."))
length + BM_Value(value, *memory, *pos)
If CountString(OID, ".") > 1
For i = 3 To n + 1
length + BM_Value(Val(StringField(OID, i, ".")), *memory, *pos)
Next
EndIf
ProcedureReturn length
EndProcedure
Procedure.l BM_Move(value.l, length.l, *memory, *pos. Reference)
; Insert byte value into memory block...
; Returns total length
MoveMemory(*memory + *pos\Value - length, *memory + *pos\Value - length + 2, length)
PokeA(*memory + *pos\Value - length, value)
PokeA(*memory + *pos\Value - length + 1, length)
*pos\Value + 2
ProcedureReturn length + 2
EndProcedure
Procedure.l BM_Null(value.l, *memory, *pos. Reference)
; Writes byte value into memory address...
; Returns length (=2)
PokeA(*memory + *pos\Value, value)
PokeA(*memory + *pos\Value + 1, 0)
*pos\Value + 2
ProcedureReturn 2
EndProcedure
CompilerIf #PB_Compiler_OS <> #PB_OS_Linux
Procedure.l Ping(n)
; Pings Station(n)\IP...
; Returns #True if a reply packet is seen from the station
Protected EchoMessage.s
Protected EchoSize.l
Protected *Echo.ICMP_ECHO_REPLY
Protected *EchoResult
Protected Handle
SelectElement(SNMPStation(), n)
With SNMPStation()
\Alive = #Station_Down
EchoMessage.s = "Ping"
EchoSize = SizeOf(ICMP_ECHO_REPLY) + Len(EchoMessage)
*EchoResult = AllocateMemory(EchoSize)
*Echo = *EchoResult
If \IP
Handle = IcmpCreateFile_()
If IcmpSendEcho_(Handle, \IP,EchoMessage, Len(EchoMessage), 0, *EchoResult, EchoSize, 500)
; Received an ICMP-Response...
; If PeekL(*EchoResult)=\IP
;...from the destination address (otherwise it will be a destination unreachable message from the default gateway)
\Alive = #Station_Up
; EndIf
EndIf
IcmpCloseHandle_(Handle)
EndIf
FreeMemory(*EchoResult)
ProcedureReturn \Alive
EndWith
EndProcedure
CompilerEndIf
Procedure.l PDU_Check(n, *memory)
Protected.a byte
Protected.i length, pos, field
Protected Value$
; Class (Bits 7/8)
; 0 - Universal (integer, string etc.)
; 1 - Application (IP address, Time Ticks)
; 2 - Context (complex data)
; 3 - Private (non standard data)
; Data Type (Bit 6)
; 0 - primitive data-type
; 1 - constructed data-type
; ASN.1 Type (Bit 5)
; 0 - no
; 1 - yes
SelectElement(SNMP(), n)
With SNMP()
pos = 0
Repeat
byte = PeekA(*memory + pos)
;Debug "Field: "+Str(field)+" Pos: "+Str(pos)+" (=$"+Hex(byte&$ff)+")"
pos + 1
length = BER_Length(*memory, @pos);
Select byte & $C0 ; mask the class...
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Case #ClassUniversal ; universal class
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If byte & #TypeConstructed ; constructed data type
; Debug "Constructed"
If byte & $10 ; ASN.1
;Debug " Length: "+Str(length)
EndIf
Else; primitive data type
Select byte & $1F
Case #TypeInteger
\ValueType = #TypeInteger
Value$ = Str(BER_Integer(*memory + pos, length))
Debug " integer: " + Value$
Case #TypeBitString
Debug " bit string"
Case #TypeOctetString
\ValueType = #TypeOctetString
Value$ = PeekS(*memory+pos, length, #PB_Ascii)
Debug " octet string: " + Value$
Case #TypeNull
Debug " null"
Case #TypeObjectIdentifier
\ValueType = #TypeObjectIdentifier
Value$ = BER_ObjectIDentifier(*memory + pos, length)
Debug " object: " + Value$
Default
Debug Str(n) + " UNKNOWN TYPE: " + Str(byte)
EndSelect
pos + length
EndIf
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Case #ClassApplication ; application
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If byte & #TypeConstructed = 0; primitive data type
Select byte & $1F
Case #TypeNetworkAddress; IPAddress
Value$ = ""
Protected i
For i = 0 To length - 1
Value$ + Str(PeekA(*memory + pos + i) )
If i < length - 1 : Value$ + "." : EndIf
Next i
\ValueType = #TypeNetworkAddress
Case #TypeCounter; Counter 32 bit
Value$ = Str(BER_Integer(*memory + pos, length))
\ValueType = #TypeCounter
Case #TypeGauge; Gauge
Value$ = Str(BER_Integer(*memory + pos, length))
\ValueType = #TypeGauge
Case #TypeTimeTicks; ticks
Value$ = Str(BER_Integer(*memory + pos, length))
\ValueType = #TypeTimeTicks
Case #TypeOpaque; Opaque
Value$ = PeekS(*memory + pos, length)
\ValueType = #TypeOpaque
; Added by Hujambo
Case #TypeCounter64; Counter 64 bit
Value$ = Str(BER_Integer(*memory + pos, length))
\ValueType = #TypeCounter64
EndSelect
EndIf
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Case #ClassContextSpecific ; context specific
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If byte & #TypeConstructed; constructed data type
Select byte & $1F
Case #PDU_RESPONSE
Debug "GetResponse PDU..."
Case #PDU_TRAP
Debug "Trap PDU..."
EndSelect
Else; primitive data type
Debug Str(n) + " ERROR: Unknown Packet"
\Status = #SNMP_Error
field = #Field_Nil
EndIf
EndSelect
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Select field
Case #Field_ErrorState
If Val(Value$)
\Status = #SNMP_Error_Name
Debug \Status
Debug Str(n) + " NAME ERROR: " + Value$
field=#Field_Nil
EndIf
Case #Field_RequestID
If Val(Value$) <> \RequestID
\Status = #SNMP_Error_RID
Debug Str(n) + " RID ERROR: " + Value$ + "~" + \RequestID
field = #Field_Nil
EndIf
Case #Field_OID
\ResponseOID = Value$
If \PDUType = #PDU_GET And Value$ <> \OID
\Status = #SNMP_Error_OID
Debug Str(n) + " OID ERROR: " + Value$ + "~" + \OID
field = #Field_Nil
EndIf
Case #Field_Variable
\Value = Value$
\Status = #SNMP_Ok
EndSelect
field + 1
Until field > #Field_Variable
ProcedureReturn \Status
EndWith
EndProcedure
Procedure.l PDU_Make(n, *memory)
; compose SNMP packet into reserved memory (548 bytes)
; n = Request number
; Returns packet length
Protected length = 0
Protected pos = 0
Protected PositionA, PositionB
Static RequestID.l = 0
SelectElement(SNMP(), n)
With SNMP()
SelectElement(SNMPStation(), \StationNumber)
RequestID + 1;Random(65535)
If RequestID = 65536 : RequestID = 0 : EndIf ; toavoid trouble wit negative values
\RequestID = RequestID
; If \GetType <> #GET_NEXT : \GetType = #GET : EndIf
; hier muss später die Gesamtlänge rein
; SNMP Version (V2.1 = 1)
length = BM_Integer($01, *memory, @pos) ; Changed by Hujambo
length = BM_Move(#TypeInteger, length, *memory, @pos)
; Community
If \PDUType = #PDU_GET Or \PDUType = #PDU_GET_NEXT
length = BM_String(SNMPStation()\ReadCommunity, *memory, @pos)
Else
length = BM_String(SNMPStation()\WriteCommunity, *memory, @pos)
EndIf
length = BM_Move(#TypeOctetString, length, *memory, @pos)
PositionA = pos ; hier muss später der Requesttype und seine Länge rein
; Request ID
length = BM_Integer(\RequestID, *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
; Error-Status
length = BM_Value(#ERRORSTATUS_NOERROR, *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
; Error-Index
length = BM_Value(0, *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
PositionB = pos ; hier muss später die Varbindlistlänge rein
; hier muss später die Länge des ersten varbinds rein
; OID
length = BM_ObjectIDentifier(\OID, *memory, @pos)
\OID = BER_ObjectIDentifier(*memory + PositionB, length); put normalized OID into database
length = BM_Move(#TypeObjectIdentifier, length, *memory, @pos)
If \PDUType = #PDU_GET Or \PDUType = #PDU_GET_NEXT
; NULL
length = BM_Null(#TypeNull, *memory, @pos)
Else
; put the value for a set request
Select \ValueType
Case #TypeInteger
length = BM_Integer(Val(\Value), *memory, @pos)
length = BM_Move(#TypeInteger, length, *memory, @pos)
Case #TypeOctetString
length = BM_String(\Value, *memory, @pos)
length = BM_Move(#TypeOctetString, length, *memory, @pos)
EndSelect
EndIf
; Envelope OID and Null-field (two times)
length = BM_Move(#ClassUniversal|#TypeConstructed|#TypeSequence, pos - PositionB, *memory, @pos) ; trage Länge Varbind ein
length = BM_Move(#ClassUniversal|#TypeConstructed|#TypeSequence, pos - PositionB, *memory, @pos) ; trage Länge Varbindlist ein
; Envelope Request-ID, Error Information and OID-Envelope
length = BM_Move(#ClassContextSpecific|#TypeConstructed|\PDUType, pos - PositionA, *memory, @pos) ; trage Typ und Länge der PDU ein
; Envelope all SNMP fields
length = BM_Move(#ClassUniversal|#TypeConstructed|#TypeSequence, pos, *memory, @pos) ; trage Gesamtlänge ein
EndWith
ProcedureReturn pos
EndProcedure
Procedure.l SNMPRequest(n)
Protected *Buffer
Protected.i Status, Start, Client
SelectElement(SNMP(), n)
With SNMP()
SelectElement(SNMPStation(), \StationNumber)
\Status = #SNMP_Unknown
Status = 0
If SNMPStation()\IP
Client = OpenNetworkConnection(SNMPStation()\IP, 161 , #PB_Network_UDP)
If Client
*Buffer = AllocateMemory(#SNMP_MaxPacketSize)
If *Buffer
Status = PDU_Make(n, *Buffer)
If Status
If SendNetworkData(Client, *Buffer, Status) = Status
Start = Date()
Repeat
Delay(1)
Status = NetworkClientEvent(Client)
If Date() - Start > 5
Debug "Timeout"
Status = #SOCKET_ERROR ; = -1 Timeout
EndIf
Until Status <> 0
If Status = #PB_NetworkEvent_Data ; 2
If ReceiveNetworkData(Client, *Buffer, #SNMP_MaxPacketSize) > 0
\Status = PDU_Check(n, *Buffer)
If \Status = #SNMP_Ok : Status = 1
Else : Status = 0 : EndIf
Else
Status = 0
\Status = -99 ; ReceiveLengthError
EndIf
Else
Status = 0
\Status = -98 ; ReceiveError #SNMP_Error
EndIf
EndIf
EndIf
FreeMemory(*Buffer)
EndIf
CloseNetworkConnection(Client)
EndIf
EndIf
EndWith
ProcedureReturn Status
EndProcedure
CompilerIf #PB_Compiler_IsMainFile
If InitNetwork()
AddElement(SNMPStation())
SNMPStation()\IP = "192.168.1.10"
SNMPStation()\ReadCommunity = "public"
SNMPStation()\WriteCommunity = "private"
AddElement(SNMP())
SNMP()\StationNumber = 0
SNMP()\OID =".1.3.6.1.2.1.2.2.1.16.10001"
SNMP()\PDUType = #PDU_GET
SNMP()\Value = ""
SNMP()\ValueType = #TypeUnknown
SNMP()\Status = #SNMP_Unknown
AddElement(SNMP())
SNMP()\StationNumber = 0
SNMP()\OID =".1.3.6.1.2.1.2.2.1.16.10002"
SNMP()\PDUType = #PDU_GET
SNMP()\Value = ""
SNMP()\ValueType = #TypeUnknown
SNMP()\Status = #SNMP_Unknown
SNMPRequest(0)
SNMPRequest(1)
If SNMP()\Status = #SNMP_Error
MessageRequester("Results", "Error")
Else
FirstElement (SNMP())
MessageRequester("Results", "Status: " + Str(SNMP()\Status) + #LF$ + "Type: " + Str(SNMP()\ValueType) + #LF$ + "Value: " + SNMP()\Value)
NextElement (SNMP())
MessageRequester("Results", "Status: " + Str(SNMP()\Status) + #LF$ + "Type: " + Str(SNMP()\ValueType) + #LF$ + "Value: " + SNMP()\Value)
EndIf
EndIf
CompilerEndIf
Re: SNMP Get
You did it in an inflexible way.
I extended my last version above to 1.22
And added a field in the station structure.
I extended my last version above to 1.22
And added a field in the station structure.