Giter Site home page Giter Site logo

snmp-server's Introduction

SNMP server

MIT license badge

Description:

Simple SNMP server in pure Python

Usage with pytest:

It is possible to use snmpserver as pytest plugin. This option requires Python >=3.6.

The fixture snmpserver has the host and port attributes (which can be set via environment variables PYTEST_SNMPSERVER_HOST and PYTEST_SNMPSERVER_PORT), along with the expect_request method:

def test_request_replies_correctly(snmpserver):
    snmpserver.expect_request("1.3.6.1.2.1.2.2.1.2", "some description")
    command = shlex.split(f'{snmpget_command} {snmpserver.host}:{snmpserver.port} IF-MIB::ifDescr')
    p = subprocess.Popen(command, stdout=subprocess.PIPE)
    p.wait()
    assert 'IF-MIB::ifDescr some description' == p.stdout.read().decode('utf-8').strip()

Standalone usage:

It is also possible to use standalone version of SNMP server, which works as an echo server if no config is passed. This version supports Python 2 and 3.

Standalone usage: snmp-server.py [-h] [-p PORT] [-c CONFIG] [-d] [-v]

SNMP server

optional arguments:
  -h, --help            show this help message and exit
  -p PORT, --port PORT  port (by default 161 - requires root privileges)
  -c CONFIG, --config CONFIG
                        OIDs config file
  -d, --debug           run in debug mode
  -v, --version         show program's version number and exit

Examples:

# ./snmp-server.py -p 12345
SNMP server listening on 0.0.0.0:12345
# ./snmp-server.py
SNMP server listening on 0.0.0.0:161

Without config file SNMP server works as a simple SNMP echo server:

# snmpget -v 2c -c public 0.0.0.0:161 1.2.3.4.5.6.7.8.9.10.11
iso.2.3.4.5.6.7.8.9.10.11 = STRING: "1.2.3.4.5.6.7.8.9.10.11"

It is possible to create a config file with values for specific OIDs.

Config file - is a Python script and must have DATA dictionary with string OID keys and values.

Values can be either ASN.1 types (e.g. integer(...), octet_string(...), etc) or any Python lambda/functions (with single argument - OID string), returning ASN.1 type.

DATA = {
  '1.3.6.1.4.1.1.1.0': integer(12345),
  '1.3.6.1.4.1.1.2.0': bit_string('\x12\x34\x56\x78'),
  '1.3.6.1.4.1.1.3.0': octet_string('test'),
  '1.3.6.1.4.1.1.4.0': null(),
  '1.3.6.1.4.1.1.5.0': object_identifier('1.3.6.7.8.9'),
  # notice the wildcards:
  '1.3.6.1.4.1.1.6.*': lambda oid: octet_string('* {}'.format(oid)),
  '1.3.6.1.4.1.1.?.0': lambda oid: octet_string('? {}'.format(oid)),
  '1.3.6.1.4.1.2.1.0': real(1.2345),
  '1.3.6.1.4.1.3.1.0': double(12345.2345),
}
# ./snmp-server.py -c config.py
SNMP server listening on 0.0.0.0:161

With config file snmpwalk command as well as snmpget can be used:

# snmpwalk -v 2c -c public 0.0.0.0:161 .1.3.6.1.4.1
iso.3.6.1.4.1.1.1.0 = INTEGER: 12345
iso.3.6.1.4.1.1.2.0 = BITS: 12 34 56 78 3 6 10 11 13 17 19 21 22 25 26 27 28
iso.3.6.1.4.1.1.3.0 = STRING: "test"
iso.3.6.1.4.1.1.4.0 = NULL
iso.3.6.1.4.1.1.5.0 = OID: iso.3.6.7.8.9
iso.3.6.1.4.1.1.6.4294967295 = STRING: "* 1.3.6.1.4.1.1.6.4294967295"
iso.3.6.1.4.1.1.9.0 = STRING: "? 1.3.6.1.4.1.1.9.0"
iso.3.6.1.4.1.2.1.0 = Opaque: Float: 1.234500
iso.3.6.1.4.1.3.1.0 = Opaque: Float: 12345.234500
iso.3.6.1.4.1.4.1.0 = No more variables left in this MIB View (It is past the end of the MIB tree)

Also snmpset command can be used:

# snmpset -v2c -c public 0.0.0.0:161 .1.3.6.1.4.1.1.3.0 s "new value"
iso.3.6.1.4.1.1.3.0 = STRING: "new value"
#
# snmpget -v2c -c public 0.0.0.0:161 .1.3.6.1.4.1.1.3.0
iso.3.6.1.4.1.1.3.0 = STRING: "new value"

License:

Released under The MIT License.

snmp-server's People

Contributors

davidventura avatar davoodeh avatar delimitry avatar oschwartz10612 avatar probert100 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

snmp-server's Issues

The server works well when I make snmpgets, but it send me exceptions when I make snmpwalks

Hello. I´m in a project where I'm trying to use your server. Right now I'm doing tests to the server with JMeter. I´m using the intraway SNMP plugin for JMeter and I can make snmpgets but not snmpwalks. JMeter said to me that it´s an exception using the get command. Maybe I have to start the server with any parameter or I don´t know. I used to start it in debug option and with a config.py file. Thanks in advance. hope to write me soon.

Gauge32 does not accept numbers between 15 and 16 bits.

It seems that the OIDS with Gauge32 does not like numbers between 32768 and 65535.
I'm assuming this is a user error and I'm feeding it incorrectly and am wondering how to do it right.

snmpset -v2c -c community_name host:port MIB::someName.1 'u' 40000

returns max value

Add snmp-server.py MVP

Implement MVP with the next functionality:

  • ASN.1 Get-Request PDU
  • ASN.1 Get-Response PDU
  • ASN.1 Get-Next-Request PDU

Add support for value ranges / enumeration

Hi
Certain OIDs only support a range of values (IF-MIB::ifAdminStatus) / enumeration, and when trying to set them with a value outside the supported values, it should fail with wrongValue.

Is it possible to add support for this?

Positive Integer values always packed as 8 octal length (64-bit) values. Breaks 32-bit tools like snmpget and snmpwalk on Windows.

_write_int() is generating 8 octet length integer tlv for all integers (greater than zero), even if it takes less than 8 octets to represent them.

I realize there is handling of negative integers going on. But this is not required based on the standard (ASN1 BER INTEGER).
Numeric data is encoded as unsigned integers with the least significant bit always first (to the right).
Partial fix is to always enable strip_trailing_zeroes. A complete fix would replicate the functionality that exists for negative numbers, for positive numbers. i.e. pack the appropriate size of bytes for the integer.

Partial fix:

add the line "strip_leading_zeros = True" at the beginning of _write_int() function

Also, there is a slight discrepancy between integer() and _write_int()

After this fix, integers on win32 netsnmp tools should work until you assign an integer value larger than 32 bits in your MIB (server config), just a heads up for everyone who is using precompiled win32 netsnmp tools on Windows.

tldr;
64-bit (8 octet) python integers making 32-bit compiled win32 netsnmp tools shart themselves.

is it possible to set community string?

snmpset -v2c -c public 0.0.0.0:161 .1.3.6.1.4.1.1.3.0 s "new value"

I noticed that the example use is passing in 'public' as the community string.
Is it possible to set it to a custom string?

snmpwalk crashes the server

Looks like after the last bugfix the snmpwalk stopped working:

running snmpwalk -v 2c -c public 0.0.0.0:161 .1.3.6.1.4.1
crashes the server

SNMP server listening on 0.0.0.0:161
Traceback (most recent call last):
  File "./snmp-server.py", line 920, in <module>
    main()
  File "./snmp-server.py", line 914, in main
    snmp_server(host, port, oids)
  File "./snmp-server.py", line 854, in snmp_server
    error_status, error_index, oid, oid_value = handle_get_next_request(oids, oid)
UnboundLocalError: local variable 'oid' referenced before assignment

Add possibility to program SNMP response value

Support config file for programming the responses.
May be, it is required to support OID wildcards 🤔
For example:

{
    "*": lambda oid: str(oid),  # to return OID string value as a response to every request
}

Or:

{
   "OID1": "string",
   "OID2": 12345,
   "OID3": Timeticks(123456000),
   "OID3": random.randint(1, 10),
}

This requires ability to write values for ASN.1 types.

  • BOOLEAN
  • INTEGER
  • BIT STRING
  • OCTET STRING
  • NULL
  • OBJECT IDENTIFIER
  • REAL
  • DOUBLE
  • IP_ADDRESS
  • TIMETICKS
  • GAUGE32
  • COUNTER32
  • COUNTER64
  • ENUMERATED
  • CHARACTER STRING

SNMPSET usage

Hello Everybody:
I started the server successfully and write my config file and I had no problems.
but when I tried to send snmpset command to OID specified in config file I got the following error:
image
I tried to change community with no effects
the command I used is:
snmpset -v2c -c private 127.0.0.1:1235 (OID) s "newValue"
Is there any possible solutions?
or snmpset is not supported in the snmp simulator?
thanks for responding

Trap server supported?

Sorry if this question is too basic but would we be able to setup a trap server with snmp-server?

Do I just use snmp-server on port 161 as the poll server and launch another snmp-server on port 162 to be used as the trap server?
Configuring each one appropriately.

Positive Integer values always packed as 8 octal length (64-bit) values. Breaks 32-bit tools like snmpget and snmpwalk on Windows.

_write_int() is generating 8 octet length integer tlv for all integers (greater than zero), even if it takes less than 8 octets to represent them.

I realize there is handling of negative integers going on.
Partial fix is to always enable strip_trailing_zeroes. A complete fix would replicate the functionality that exists for negative numbers, for positive numbers. i.e. pack the appropriate size of bytes for the integer.

Partial fix:

add the line "strip_leading_zeros = True" at the beginning of _write_int() function

Also, there is a slight discrepancy between integer() and _write_int()

After this fix, integers on win32 netsnmp tools should work until you assign an integer value larger than 32 bits in your MIB (server config), just a heads up for everyone who is using precompiled win32 netsnmp tools on Windows.

tldr;
64-bit (8 octet) python integers making 32-bit compiled win32 netsnmp tools shart themselves.

Update README

After MVP and tests add - add usage examples and update description.

Responds to only first value when snmpget with multi OID is used

Sample Request: snmpget -v 2c -c public 10.16.178.241 1.3.6.1.2.1.1.2.0 1.3.6.1.2.1.1.3.0
Responds only with value for first OID

SNMPv2-MIB::sysObjectID.0 = OID: SNMPv2-SMI::enterprises.9.1.123
[root@centos ~]# 

config_file:

# SNMP server response config example

def my_response(oid):
	res = '|'.join(oid.split('.'))
	return octet_string('response: {}'.format(res))


DATA = {
    '1.3.6.1.4.1.1.1.0': integer(12345),
    '1.3.6.1.4.1.1.2.0': bit_string('\x12\x34\x56\x78'),
    '1.3.6.1.4.1.1.3.0': octet_string('test'),
    '1.3.6.1.4.1.1.4.0': null(),
    '1.3.6.1.4.1.1.5.0': object_identifier('1.3.6.7.8.9'),
    '1.3.6.1.4.1.1.6.0': real(1.2345),
    '1.3.6.1.4.1.1.7.0': double(12345.2345),
    # notice the wildcards in the next OIDs:
    '1.3.6.1.4.1.1.?.0': lambda oid: octet_string('? {}'.format(oid)),
    '1.3.6.1.4.1.2.*': lambda oid: octet_string('* {}'.format(oid)),
    # lambda or function, with single oid argument, can be used for response generation
    '1.3.6.1.4.1.1001.1.0': my_response,
    '1.3.6.1.4.1.1002.1.0': lambda oid: octet_string('-'.join(oid.split('.'))),

  '1.3.6.1.2.1.1.2.0':  object_identifier('1.3.6.1.4.1.9.1.123'),  
    '1.3.6.1.2.1.1.3.0': timeticks(2093505391),
}

Here is the debug output:

[DEBUG] Received 57 bytes from ('198.18.134.29', 35359)
[DEBUG] ASN1_SEQUENCE: length = 55
[DEBUG] ASN1_INTEGER: 1
[DEBUG] ASN1_OCTET_STRING: public
[DEBUG] ASN1_GET_REQUEST_PDU: length = 42
[DEBUG] ASN1_INTEGER: 22870771
[DEBUG] ASN1_INTEGER: 0
[DEBUG] ASN1_INTEGER: 0
[DEBUG] ASN1_SEQUENCE: length = 28
[DEBUG] ASN1_SEQUENCE: length = 12
[DEBUG] ASN1_OBJECT_IDENTIFIER: 1.3.6.1.2.1.1.2.0
[DEBUG] ASN1_NULL: 0
[DEBUG] ASN1_SEQUENCE: length = 12
[DEBUG] ASN1_OBJECT_IDENTIFIER: 1.3.6.1.2.1.1.3.0
[DEBUG] ASN1_NULL: 0
[DEBUG] Sending 51 bytes of response
[DEBUG] 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.