{"id":13650503,"url":"https://github.com/swisscom/ruby-netsnmp","last_synced_at":"2025-04-22T18:31:48.080Z","repository":{"id":10921472,"uuid":"56509904","full_name":"swisscom/ruby-netsnmp","owner":"swisscom","description":"SNMP library in ruby (v1, v2c, v3)","archived":true,"fork":false,"pushed_at":"2023-04-07T11:35:52.000Z","size":434,"stargazers_count":33,"open_issues_count":7,"forks_count":24,"subscribers_count":14,"default_branch":"master","last_synced_at":"2024-01-25T02:11:20.953Z","etag":null,"topics":["netsnmp","nonblocking","pure-ruby","ruby","snmp","snmp-protocol"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/swisscom.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS"}},"created_at":"2016-04-18T13:27:01.000Z","updated_at":"2023-10-03T19:36:16.000Z","dependencies_parsed_at":"2024-01-03T05:07:41.811Z","dependency_job_id":"58a3e317-a9b1-442b-9c53-02c8de470fa0","html_url":"https://github.com/swisscom/ruby-netsnmp","commit_stats":null,"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisscom%2Fruby-netsnmp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisscom%2Fruby-netsnmp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisscom%2Fruby-netsnmp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisscom%2Fruby-netsnmp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/swisscom","download_url":"https://codeload.github.com/swisscom/ruby-netsnmp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223903109,"owners_count":17222491,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["netsnmp","nonblocking","pure-ruby","ruby","snmp","snmp-protocol"],"created_at":"2024-08-02T02:00:37.361Z","updated_at":"2024-11-10T01:30:51.005Z","avatar_url":"https://github.com/swisscom.png","language":"Ruby","funding_links":[],"categories":["Libraries"],"sub_categories":["Ruby"],"readme":"# netsnmp\n\n[![Gem Version](https://badge.fury.io/rb/netsnmp.svg)](http://rubygems.org/gems/netsnmp)\n![Tests](https://github.com/swisscom/ruby-netsnmp/workflows/Tests/badge.svg)\n[![Code Climate](https://codeclimate.com/github/swisscom/ruby-netsnmp/badges/gpa.svg)](https://codeclimate.com/github/swisscom/ruby-netsnmp)\n[![Docs](http://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/github/swisscom/ruby-netsnmp/master)\n\nThe `netsnmp` gem provides a ruby native implementation of the SNMP protocol (v1/2c abd v3).\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'netsnmp'\n```\n\nAnd then execute:\n\n```\n$ bundle\n```\n\nOr install it yourself as:\n\n```\n$ gem install netsnmp\n```\n\n## Features\n\nThis gem provides:\n\n* Implementation in ruby of the SNMP Protocol for v3, v2c and v1 (most notable the rfc3414 and 3826).\n* SNMPv3 USM supporting MD5/SHA/SHA256 auth and DES/AES128 privacy crypto algorithms.\n* Client/Manager API with simple interface for get, genext, set and walk.\n* Pure Ruby.\n* Support for concurrency and evented I/O.\n\n## Why?\n\nIf you look for snmp gems in ruby toolbox, you'll find a bunch.\nYou may ask, why not just use one of them?\n\nMost of them only implement v1 and v2, so if your requirement is to use v3, you're left with only 2 choices: [net-snmp](https://github.com/mixtli/net-snmp) (unmantained since 2013) and its follow-up [net-snmp2](https://github.com/jbreeden/net-snmp2), which started as a fork to fix some bugs left unattended. Both libraries wrap the C netsnmp library using FFI, which leaves them vulnerable to the following bugs (experienced in both libraries):\n\n* Dependency of specific versions of netsnmp C package.\n* Memory Leaks.\n* Doesn't work reliable in ruby \u003e 2.0.0-p576, crashing the VM.\n* Network I/O done by the library, thereby blocking the GVL, thereby making all snmp calls block the whole ruby VM.\n  * This means, multi-threading is impossible.\n  * This means, evented I/O is impossible.\n\nAll of these issues are resolved here.\n\n## Features\n\n* Client Interface, which supports SNMP v3, v2c, and v1\n* Supports get, getnext, set and walk calls\n* MIB support\n* Proxy IO object support (for eventmachine/celluloid-io)\n* Ruby \u003e= 2.1 support (modern)\n* Pure Ruby (no FFI)\n* Easy PDU debugging\n\n## Examples\n\nYou can use the docker container provided under spec/support to test against these examples (the port used in the examples should be the docker external port mapped to port 161).\n\n```ruby\nrequire 'netsnmp'\n\n# example you can test against the docker simulator provided. port attribute might be different.\nmanager = NETSNMP::Client.new(host: \"localhost\", port: 33445, username: \"simulator\",\n                              auth_password: \"auctoritas\", auth_protocol: :md5,\n                              priv_password: \"privatus\", priv_protocol: :des,\n                              context: \"a172334d7d97871b72241397f713fa12\")\n\n# SNMP get\nmanager.get(oid: \"sysName.0\") #=\u003e 'tt'\n\n# SNMP walk\n# sysORDescr\nmanager.walk(oid: \"sysORDescr\").each do |oid_code, value|\n  # do something with them\n  puts \"for #{oid_code}: #{value}\"\nend\n\nmanager.close\n\n# SNMP set\nmanager2 = NETSNMP::Client.new(host: \"localhost\", port: 33445, username: \"simulator\",\n                               auth_password: \"auctoritas\", auth_protocol: :md5,\n                               priv_password: \"privatus\", priv_protocol: :des,\n                               context: \"0886e1397d572377c17c15036a1e6c66\")\n\n# setting to 43, becos yes\n# sysUpTimeInstance\nmanager2.set(\"1.3.6.1.2.1.1.3.0\", value: 43)\n\nmanager2.close\n```\n\nSNMP v2/v1 examples will be similar (beware of the differences in the initialization attributes).\n\n## SNMP Application Types\n\nAll previous examples were done specifying primitive types, i.e. unless specified otherwise, it's gonna try to convert a ruby \"primitive\" type to an ASN.1 primitive type, and vice-versa:\n\n* Integer      -\u003e ASN.1 Integer\n* String      -\u003e ASN.1 Octet String\n* nil         -\u003e ASN.1 Null\n* true, false -\u003e ASN.1 Boolean\n\nThat means that, if you pass `value: 43` to the `#set` call, it's going to build a varbind with an ASN.1 Integer. If You issue a `#get` and the response contains an ASN.1 Integer, it's going to return an Integer.\n\nHowever, SNMP defines application-specific ASN.1 types, for which there is support, albeit limited. Currently, there is support for ip addresses and timeticks.\n\n* IPAddr -\u003e ASN.1 context-specific\n\nIf you create an `IPAddr` object (ruby standard library `ipaddr`) and pass it to the `#set` call, it will map to the SNMP content-specific code. If the response of a `#get` call contains an ip address, it will map to an `IPAddr` object.\n\n* NETSNMP::Timeticks -\u003e ASN.1 content-specific\n\nThe `NETSNMP::Timeticks` type is internal to this library, but it is a ruby `Numeric` type. You are safe to use it \"as a numeric\", that is, perform calculations.\n\n\nCounter32 and Counter64 types will map to plain integers.\n\nYou can find usage examples [here](https://github.com/swisscom/ruby-netsnmp/blob/master/spec/varbind_spec.rb). If you need support to a missing type, you have the following options:\n\n* Use the `:type` parameter in `#set` calls:\n```ruby\n# as a symbol\nmanager.set(\"somecounteroid\", value: 999999, type: :counter64)\n# as the SNMP specific type id, if you're familiar with the protocol\nmanager.set(\"somecounteroid\", value: 999999, type: 6)\n```\n* Fork this library, extend support, write a test and submit a PR (the desired solution ;) )\n\n## MIB\n\n`netsnmp` will load the default MIBs from known or advertised (via `MIBDIRS`) directories (provided that they're installed in the system). These will be used for the OID conversion.\n\nSometimes you'll need to load more, your own MIBs, in which case, you can use the following API:\n\n```ruby\nrequire \"netsnmp\"\n\nNETSNMP::MIB.load(\"MY-MIB\")\n# or, if it's not in any of the known locations\nNETSNMP::MIB.load(\"/path/to/MY-MIB.txt\")\n```\n\nYou can install common SNMP mibs by using your package manager:\n\n```\n# using apt-get\n\u003e apt-get install snmp-mibs-downloader\n# using apk\n\u003e apk --update add net-snmp-libs\n```\n\n## Concurrency\n\nIn ruby, you are usually adviced not to share IO objects across threads. The same principle applies here to `NETSNMP::Client`: provided you use it within a thread of execution, it should behave safely. So, something like this would be possible:\n\n```ruby\ngeneral_options = { auth_protocol: ....\nrouters.map do |r|\n  Thread.start do\n    NETSNMP::Client.new(general_options.merge(host: r)) do |cl|\n      cli.get(oid: \"1.6.3.......\n\n    end\n  end\nend.each(\u0026:join)\n```\n\nEvented IO is also supported, in that you can pass a `:proxy` object as an already opened channel of communication to the client. Very important: you have to take care of the lifecycle, as the client will not connect and will not close the object, it will assume no control over it.\n\nWhen passing a proxy object, you can omit the `:host` parameter.\n\nThe proxy object will have to be a duck-type implementing `#send`, which is a method receiving the sending PDU payload, and return the payload of the receiving PDU.\n\nHere is a small pseudo-code example:\n\n```ruby\n# beware, we are inside a warp-speed loop!!!\ngeneral_options = { auth_protocol: ....\nproxy = SpecialUDPImplementation.new(host: router)\nNETSNMP::Client.new(general_options.merge(proxy: proxy)) do |cl|\n  # this get call will eventually #send to the proxy...\n  cli.get(oid: \"1.6.3.......\n\nend\n# client isn't usable anymore, but now we must close to proxy\nproxy.close\n```\n\nFor more information about this subject, the specs test this feature against celluloid-io. An eventmachine could be added, if someone would be kind enough to provide an implementation.\n\n## Performance\n\n\n### XOR\n\nThis library has some workarounds to some missing features in the ruby language, namely the inexistence of a byte array structure. The closest we have is a byte stream presented as a String with ASCII encoding. A method was added to the String class called `#xor` for some operations needed internally. To prevent needless monkey-patches, Refinements have been employed.\n\nIf `#xor` becomes at some point the bottleneck of your usage, this gem has also support for [xorcist](https://github.com/fny/xorcist/). You just have to add it to your Gemfile (or install it in the system):\n\n```\n# Gemfile\n\ngem 'netsnmp'\n\n# or, in the command line\n\n$ gem install netsnmp\n```\n\nand `netsnmp` will automatically pick it up.\n\n## Auth/Priv Key\n\nIf you'll use this gem often with SNMP v3 and auth/priv security level enabled, you'll have that funny feeling that everything could be a bit faster. Well, this is basically because the true performance bottleneck of this gem is the generation of the auth and pass keys used for authorization and encryption. Although this is a one-time thing for each client, its lag will be noticeable if you're running on \u003e 100 hosts.\n\nThere is a recommended work-around, but this is only usable **if you are using the same user/authpass/privpass on all the hosts!!!**. Use this with care, then:\n\n```ruby\n$shared_security_parameters = NETSNMP::SecurityParameters.new(security_level: :authpriv, username: \"mustermann\",\n                                                              auth_protocol: :md5, priv_protocol: :aes, ....\n# this will eager-load the auth/priv_key\n...\n\n# over 9000 routers are running on this event loop!!! this is just one!\nNETSNMP::Client.new(share_options.merge(proxy: router_proxy, security_parameters: $shared_security_parameters.dup).new do |cl|\n  cli.get(oid:  .....\nend\n```\n\n## Compatibility\n\nThis library supports and is tested against ruby versions 2.1 or more recent, including ruby 3. It also supports and tests against Truffleruby.\n\n## OpenSSL\n\nAll encoding/decoding/encryption/decryption/digests are done using `openssl`, which is (still) a part of the standard library. If at some point `openssl` is removed and not specifically distributed, you'll have to install it yourself. Hopefully this will never happen.\n\nIt also uses the `openssl` ASN.1 API to encode/decode BERs, which is known to be strict, and [may not be able to decode PDUs if not compliant with the supported RFC](https://github.com/swisscom/ruby-netsnmp/issues/47).\n\n## Debugging\n\nYou can either set the `NETSNMP_DEBUG` to the desided debug level (currently, 1 and 2). The logs will be written to stderr.\n\nYou can also set it for a specific client:\n\n```ruby\nmanager2 = NETSNMP::Client.new(debug: $stderr, debug_level: 2, ....)\n```\n\n\n## Tests\n\nThis library uses RSpec. The client specs are \"integration\" tests, in that we communicate with an [snmpsim-built snmp agent simulator](https://github.com/etingof/snmpsim).\n\n\n### RSpec\n\nYou can run all tests by typing:\n\n```\n\u003e bundle exec rake spec\n# or\n\u003e bundle exec rspec\n...\n```\n\n\n### Docker\n\nThe most straightforward way of running the tests is by using the `docker-compose` setup (which is also what's used in the CI). Run it against the ruby version you're targeting:\n\n```\n\u003e docker-compose -f docker-compose.yml -f docker-compose-ruby-${RUBY_MAJOR_VERSION}.${RUBY_MAJOR_VERSION}.yml run netsnmp\n```\n\nThe CI runs the tests against all supported ruby versions. If changes break a specific version of ruby, make sure you commit appropriate changes addressing the edge case, or let me know in the issues board, so I can help.\n\n### SNMP Simulator\n\nThe SNMP simulator runs in its own container in the `docker` setup.\n\nYou can install the package yourself (ex: `pip install snmpsim`) and run the server locally, and then set the `SNMP_PORT` environment variable, where the snmp simulator is running.\n\n#### CI\n\nThe job of the CI is:\n\n* Run all the tests;\n* Make sure the tests cover an appropriate surface of the code;\n* Lint the code;\n* (for ruby 3.0) type check the code;\n\n\n## Contributing\n\n* Fork this repository\n* Make your changes and send me a pull request\n* If I like them I'll merge them\n* If I've accepted a patch, feel free to ask for a commit bit!\n\n## TODO\n\nThere are some features which this gem doesn't support. It was built to provide a client (or manager, in SNMP language) implementation only, and the requirements were fulfilled. However, these notable misses will stand-out:\n\n* No server (Agent, in SNMP-ish) implementation.\n* No getbulk support.\n\nSo if you like the gem, but would rather have these features implemented, please help by sending us a PR and we'll gladly review it.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswisscom%2Fruby-netsnmp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fswisscom%2Fruby-netsnmp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswisscom%2Fruby-netsnmp/lists"}