Skip to content

Gufo SNMP Example: SNMPv3 Get Request

In the previous example, we demonstrated how to request a single item using SNMP v2c in Gufo SNMP. Now, we'll show you how to achieve the same with SNMP v3, which offers a similar API with additional authentication options.

Despite SNMP v3's increased complexity, Gufo SNMP effectively handles all the intricacies, making SNMP v3 operations as straightforward as v2c. Let's modify our previous example to utilize SNMP v3.

get.py
import sys

from gufo.snmp import Aes128Key, DesKey, Md5Key, Sha1Key, User
from gufo.snmp.sync import SnmpSession

AUTH_ALG = {
    "md5": Md5Key,
    "sha": Sha1Key,
}

PRIV_ALG = {
    "des": DesKey,
    "aes128": Aes128Key,
}


def get_user() -> User:
    name = sys.argv[2]
    if len(sys.argv) > 4:
        auth_alg, key = sys.argv[4].split(":", 1)
        auth_key = AUTH_ALG[auth_alg](key.encode())
    else:
        auth_key = None
    if len(sys.argv) > 5:
        priv_alg, key = sys.argv[5].split(":", 1)
        priv_key = PRIV_ALG[priv_alg](key.encode())
    else:
        priv_key = None
    return User(name, auth_key=auth_key, priv_key=priv_key)


def main(addr: str, user: User, oid: str) -> None:
    with SnmpSession(addr=addr, user=user) as session:
        r = session.get(oid)
        print(r)


main(sys.argv[1], get_user(), sys.argv[3])

Let's see the details.

get.py
import sys

from gufo.snmp import Aes128Key, DesKey, Md5Key, Sha1Key, User
from gufo.snmp.sync import SnmpSession

AUTH_ALG = {
    "md5": Md5Key,
    "sha": Sha1Key,
}

PRIV_ALG = {
    "des": DesKey,
    "aes128": Aes128Key,
}
Import sys module to parse the CLI argument.

Warning

We use sys.argv only for demonstration purposes. Use argparse or alternatives in real-world applications.

get.py
import sys

from gufo.snmp import Aes128Key, DesKey, Md5Key, Sha1Key, User
from gufo.snmp.sync import SnmpSession

AUTH_ALG = {
    "md5": Md5Key,
    "sha": Sha1Key,
}

PRIV_ALG = {
    "des": DesKey,
    "aes128": Aes128Key,
}
We need to import User class and key algorithm helpers.

get.py
import sys

from gufo.snmp import Aes128Key, DesKey, Md5Key, Sha1Key, User
from gufo.snmp.sync import SnmpSession

AUTH_ALG = {
    "md5": Md5Key,
    "sha": Sha1Key,
}

PRIV_ALG = {
    "des": DesKey,
    "aes128": Aes128Key,
}

SnmpSession object holds all necessary API. We're using a synchronous version from gufo.snmp.sync.

get.py
import sys

from gufo.snmp import Aes128Key, DesKey, Md5Key, Sha1Key, User
from gufo.snmp.sync import SnmpSession

AUTH_ALG = {
    "md5": Md5Key,
    "sha": Sha1Key,
}

PRIV_ALG = {
    "des": DesKey,
    "aes128": Aes128Key,
}

SNMPv3 offers various authentication options, so we define mappings between human-readable names and Gufo SNMP key wrappers to use later in the get_user function.

get.py
import sys

from gufo.snmp import Aes128Key, DesKey, Md5Key, Sha1Key, User
from gufo.snmp.sync import SnmpSession

AUTH_ALG = {
    "md5": Md5Key,
    "sha": Sha1Key,
}

PRIV_ALG = {
    "des": DesKey,
    "aes128": Aes128Key,
}
Similarly, SNMPv3 offers various privacy options, and we create mappings between human-readable names and key wrappers for these privacy options.

get.py
def get_user() -> User:
    name = sys.argv[2]
    if len(sys.argv) > 4:
        auth_alg, key = sys.argv[4].split(":", 1)
        auth_key = AUTH_ALG[auth_alg](key.encode())
    else:
        auth_key = None
    if len(sys.argv) > 5:
        priv_alg, key = sys.argv[5].split(":", 1)
        priv_key = PRIV_ALG[priv_alg](key.encode())
    else:
        priv_key = None
    return User(name, auth_key=auth_key, priv_key=priv_key)

While SNMP v2c relies on a simple community string for authentication, SNMPv3 introduces the more intricate User-Based Security Model (USM). In this model, a user typically consists of a username, along with optional authentication and privacy options. Gufo SNMP encapsulates these details within the User class.

To facilitate the configuration process, we define the get_user function. This function processes command-line arguments and returns an instance of the User class.

get.py
def get_user() -> User:
    name = sys.argv[2]
    if len(sys.argv) > 4:
        auth_alg, key = sys.argv[4].split(":", 1)
        auth_key = AUTH_ALG[auth_alg](key.encode())
    else:
        auth_key = None
    if len(sys.argv) > 5:
        priv_alg, key = sys.argv[5].split(":", 1)
        priv_key = PRIV_ALG[priv_alg](key.encode())
    else:
        priv_key = None
    return User(name, auth_key=auth_key, priv_key=priv_key)
We get the user name from the second command-line positional parameter.

get.py
def get_user() -> User:
    name = sys.argv[2]
    if len(sys.argv) > 4:
        auth_alg, key = sys.argv[4].split(":", 1)
        auth_key = AUTH_ALG[auth_alg](key.encode())
    else:
        auth_key = None
    if len(sys.argv) > 5:
        priv_alg, key = sys.argv[5].split(":", 1)
        priv_key = PRIV_ALG[priv_alg](key.encode())
    else:
        priv_key = None
    return User(name, auth_key=auth_key, priv_key=priv_key)
Authentication options are optional, so we check the fourth command-line parameter.

get.py
def get_user() -> User:
    name = sys.argv[2]
    if len(sys.argv) > 4:
        auth_alg, key = sys.argv[4].split(":", 1)
        auth_key = AUTH_ALG[auth_alg](key.encode())
    else:
        auth_key = None
    if len(sys.argv) > 5:
        priv_alg, key = sys.argv[5].split(":", 1)
        priv_key = PRIV_ALG[priv_alg](key.encode())
    else:
        priv_key = None
    return User(name, auth_key=auth_key, priv_key=priv_key)
If an authentication option is set, it must have the format <alg>:<key>, where:

  • <alg> - authentication algorithm, which must be one of AUTH_ALG keys.
  • <key> - an authentication key.

Note

SNMPv3 introduces three forms of keys:

* Password
* Master key
* Localized key

Choose the form deliberately. Gufo SNMP supports all three forms, which can be selected through optional parameters of the *Key classes. This example uses the default form: a password.

Then we find a proper key class via AUTH_ALG mapping and pass a key.

Note

All keys in Gufo SNMP are passed as bytes, so we use .encode() method to convert from str.

get.py
def get_user() -> User:
    name = sys.argv[2]
    if len(sys.argv) > 4:
        auth_alg, key = sys.argv[4].split(":", 1)
        auth_key = AUTH_ALG[auth_alg](key.encode())
    else:
        auth_key = None
    if len(sys.argv) > 5:
        priv_alg, key = sys.argv[5].split(":", 1)
        priv_key = PRIV_ALG[priv_alg](key.encode())
    else:
        priv_key = None
    return User(name, auth_key=auth_key, priv_key=priv_key)

If no authentication key is provided, set it to None to disable authentication.

get.py
def get_user() -> User:
    name = sys.argv[2]
    if len(sys.argv) > 4:
        auth_alg, key = sys.argv[4].split(":", 1)
        auth_key = AUTH_ALG[auth_alg](key.encode())
    else:
        auth_key = None
    if len(sys.argv) > 5:
        priv_alg, key = sys.argv[5].split(":", 1)
        priv_key = PRIV_ALG[priv_alg](key.encode())
    else:
        priv_key = None
    return User(name, auth_key=auth_key, priv_key=priv_key)
Privacy settings are handled just like authentication settings. We expect them in the fifth command-line parameter, and then use PRIV_ALG mapping to get a proper algorithm.

As with authentication, None means no encryption.

get.py
def get_user() -> User:
    name = sys.argv[2]
    if len(sys.argv) > 4:
        auth_alg, key = sys.argv[4].split(":", 1)
        auth_key = AUTH_ALG[auth_alg](key.encode())
    else:
        auth_key = None
    if len(sys.argv) > 5:
        priv_alg, key = sys.argv[5].split(":", 1)
        priv_key = PRIV_ALG[priv_alg](key.encode())
    else:
        priv_key = None
    return User(name, auth_key=auth_key, priv_key=priv_key)
Then we construct and return a User instance.

get.py
def main(addr: str, user: User, oid: str) -> None:
    with SnmpSession(addr=addr, user=user) as session:
        r = session.get(oid)
        print(r)

We define our main function and expect the following arguments:

  • Address of the agent.
  • User instance.
  • OID to query.
get.py
def main(addr: str, user: User, oid: str) -> None:
    with SnmpSession(addr=addr, user=user) as session:
        r = session.get(oid)
        print(r)

First, we need to create SnmpSession object which wraps the client's session. The SnmpSession may be used as an instance directly or operated as context manager using the with clause. When used as a context manager, the client automatically closes all connections on the exit of context, so its lifetime is defined explicitly.

SnmpSession constructor offers lots of configuration variables for fine-tuning. Refer to the SnmpSession reference for further details. In our example, we set the agent's address and User instance.

get.py
def main(addr: str, user: User, oid: str) -> None:
    with SnmpSession(addr=addr, user=user) as session:
        r = session.get(oid)
        print(r)

We use SnmpSession.get() function to query OID. See SnmpSession.get() reference for further details.

get.py
def main(addr: str, user: User, oid: str) -> None:
    with SnmpSession(addr=addr, user=user) as session:
        r = session.get(oid)
        print(r)

It is up to the application how to deal with the result. In our example we just print it.

get.py
main(sys.argv[1], get_user(), sys.argv[3])

Let's run our main() function. Pass the address as the first command-line parameter, construct the user with get_user(), and pass the OID as the third parameter.

Running

Let's check our script. Run example as:

$ python3 examples/sync/get-v3.py 127.0.0.1 public 1.3.6.1.2.1.1.6.0 sha:12345678 aes128:87654321
Gufo SNMP Test