SNMP Get

Just starting out? Need help? Post your questions and find answers here.
User avatar
hujambo
User
User
Posts: 48
Joined: Wed May 15, 2013 8:26 pm
Location: South Pacific

SNMP Get

Post by hujambo »

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
TassyJim
Enthusiast
Enthusiast
Posts: 151
Joined: Sun Jun 16, 2013 6:27 am
Location: Tasmania (Australia)

Re: SNMP Get

Post by TassyJim »

I cheated and used net-SNMP

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$
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
User avatar
hujambo
User
User
Posts: 48
Joined: Wed May 15, 2013 8:26 pm
Location: South Pacific

Re: SNMP Get

Post by hujambo »

Thanks Jim,

If all else fails that will be my option too :)
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: SNMP Get

Post by infratec »

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
User avatar
hujambo
User
User
Posts: 48
Joined: Wed May 15, 2013 8:26 pm
Location: South Pacific

Re: SNMP Get

Post by hujambo »

Thanks for the link Infratec. Yes, I've tried that example and couldn't get it working.
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: SNMP Get

Post by infratec »

Do you have snmpwalk running?
Then the example from Michael should also work.
User avatar
hujambo
User
User
Posts: 48
Joined: Wed May 15, 2013 8:26 pm
Location: South Pacific

Re: SNMP Get

Post by hujambo »

Yes Infratec, SNMP Walk working. I'll revisit Michael's code. All I was getting was 0's

Thanks again,

Ed
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: SNMP Get

Post by infratec »

An extended version of Michaels original:

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
This works for me.
User avatar
hujambo
User
User
Posts: 48
Joined: Wed May 15, 2013 8:26 pm
Location: South Pacific

Re: SNMP Get

Post by hujambo »

Thank you Infratec, that is perfect.

Thanks again for your help,

Ed
User avatar
hujambo
User
User
Posts: 48
Joined: Wed May 15, 2013 8:26 pm
Location: South Pacific

Re: SNMP Get

Post by hujambo »

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
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: SNMP Get

Post by infratec »

The Get, GetNext, and Set operations used in SNMPv1 are identical as those used in SNMPv2c.
https://www.dpstele.com/snmp/v1-v2c-v3-difference.php

Did you use WireShark to see what happens?

Or do you really need v2 and not v2c ?

Personally I only know v2c devices.
User avatar
hujambo
User
User
Posts: 48
Joined: Wed May 15, 2013 8:26 pm
Location: South Pacific

Re: SNMP Get

Post by hujambo »

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
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: SNMP Get

Post by infratec »

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.
User avatar
hujambo
User
User
Posts: 48
Joined: Wed May 15, 2013 8:26 pm
Location: South Pacific

Re: SNMP Get

Post by hujambo »

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


infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: SNMP Get

Post by infratec »

You did it in an inflexible way.

I extended my last version above to 1.22
And added a field in the station structure.
Post Reply