BSNMPCLIENT(3)
π BSNMPCLIENT(3) β SNMP client library π
Okay bestie, welcome to BSNMPCLIENT(3) from the Library Functions Manual. This is the SNMP client library, aka the toolkit for building SNMP client apps that speak SNMP versions 1, 2 or 3. We are going through every function and every field, no skipped lore, no cap. β¨
πΈ NAME πΈ
The whole squad on this page is: snmp_client, snmp_client_init, snmp_client_set_host, snmp_client_set_port, snmp_send_cb_f, snmp_timeout_cb_f, snmp_timeout_start_f, snmp_timeout_stop_f, snmp_open, snmp_close, snmp_pdu_create, snmp_add_binding, snmp_pdu_check, snmp_pdu_send, snmp_oid_append, snmp_parse_server, snmp_receive, snmp_table_cb_f, snmp_table_fetch, snmp_table_fetch_async, snmp_dialog, and snmp_discover_engine β all of that is the SNMP client library, periodt. π
π LIBRARY π
Begemot SNMP library (libbsnmp, -lbsnmp) β that's the one, bestie. π«Ά
β¨ SYNOPSIS β¨
Here's the full menu, copied exactly so you can yeet it into your code without typos:
#include <asn1.h>
#include <snmp.h>
#include <snmpclient.h>
typedef void
(*snmp_send_cb_f)(struct snmp_pdu *req, struct snmp_pdu *resp,
void *uarg);
typedef void
(*snmp_timeout_cb_f)(void *uarg);
typedef void *
(*snmp_timeout_start_f)(struct timeval *timeout,
snmp_timeout_cb_f callback, void *uarg);
typedef void
(*snmp_timeout_stop_f)(void *timeout_id);
extern struct snmp_client snmp_client;
void
snmp_client_init(struct snmp_client *client);
int
snmp_client_set_host(struct snmp_client *client, const char *host);
int
snmp_client_set_port(struct snmp_client *client, const char *port);
int
snmp_open(const char *host, const char *port,
const char *read_community, const char *write_community);
void
snmp_close(void);
void
snmp_pdu_create(struct snmp_pdu *pdu, u_int op);
int
snmp_add_binding(struct snmp_pdu *pdu, ...);
int
snmp_pdu_check(const struct snmp_pdu *req, const struct snmp_pdu *resp);
int32_t
snmp_pdu_send(struct snmp_pdu *pdu, snmp_send_cb_f func, void *uarg);
int
snmp_oid_append(struct asn_oid *oid, const char *fmt, ...);
int
snmp_parse_server(struct snmp_client *sc, const char *str);
int
snmp_receive(int blocking);
typedef void
(*snmp_table_cb_f)(void *list, void *arg, int res);
int
snmp_table_fetch(const struct snmp_table *descr, void *list);
int
snmp_table_fetch_async(const struct snmp_table *descr, void *list,
snmp_table_cb_f callback, void *uarg);
int
snmp_dialog(struct snmp_pdu *req, struct snmp_pdu *resp);
int
snmp_discover_engine(void);
π DESCRIPTION π
The SNMP library contains routines to easily build SNMP client applications that use SNMP versions 1, 2 or 3. Most of the routines use a struct snmp_client:
struct snmp_client {
enum snmp_version version;
int trans; /* which transport to use */
/* these two are read-only for the application */
char *cport; /* port number as string */
char *chost; /* host name or IP address as string */
char read_community[SNMP_COMMUNITY_MAXLEN + 1];
char write_community[SNMP_COMMUNITY_MAXLEN + 1];
/* SNMPv3 specific fields */
int32_t identifier;
int32_t security_model;
struct snmp_engine engine;
struct snmp_user user;
/* SNMPv3 Access control - VACM*/
uint32_t clen;
uint8_t cengine[SNMP_ENGINE_ID_SIZ];
char cname[SNMP_CONTEXT_NAME_SIZ];
struct timeval timeout;
u_int retries;
int dump_pdus;
size_t txbuflen;
size_t rxbuflen;
int fd;
int32_t next_reqid;
int32_t max_reqid;
int32_t min_reqid;
char error[SNMP_STRERROR_LEN];
snmp_timeout_start_f timeout_start;
snmp_timeout_stop_f timeout_stop;
char local_path[SUNPATHLEN];
};
The fields of this structure are described below, bestie β every single one, fr fr.
versionβ this is the version of SNMP to use. Seebsnmplib(3)for applicable values. The default version isSNMP_V2c.transβ the transport vibe check. If this isSNMP_TRANS_LOC_DGRAM, a local datagram socket is used. If it isSNMP_TRANS_LOC_STREAM, a local stream socket is used. ForSNMP_TRANS_UDP, a UDPv4 socket is created; forSNMP_TRANS_UDP6, a UDPv6 socket is created. For local sockets it uses thechostfield as the path to the server's socket.cportβ the SNMP agent's UDP port number. This may be a symbolic port number (from/etc/services) or a numeric port number. If this field isNULL(the default), the standard SNMP port is used. This field should not be changed directly β callsnmp_client_set_port()instead.chostβ the SNMP agent's host name, IP address or Unix domain socket path name. If this isNULL(the default), localhost is assumed. This field should not be changed directly β go throughsnmp_client_set_host().read_communityβ the community name to be used for all requests except SET requests. The default ispublic.write_communityβ the community name to be used for SET requests. The default isprivate.identifierβ the message identifier value to be used with SNMPv3 PDUs. It gets incremented with each transmitted PDU.security_modelβ the security model to be used with SNMPv3 PDUs. Currently only the User-Based Security model specified by RFC 3414 (value 3) is supported.engineβ the authoritive SNMP engine parameters to be used with SNMPv3 PDUs.userβ the USM SNMP user credentials to be used with SNMPv3 PDUs.clenβ the length of the context engine id to be used with SNMPv3 PDUs.cengineβ the context engine id to be used with SNMPv3 PDUs. Default is empty.cnameβ the context name to be used with SNMPv3 PDUs. Default is''.timeoutβ the maximum time to wait for responses to requests. If the time elapses, the request is resent up toretriestimes. The default is 3 seconds.retriesβ number of times a request PDU is to be resent. If set to 0, the request is sent only once. The default is 3 retransmissions.dump_pdusβ if set to a non-zero value, all received and sent PDUs are dumped viasnmp_pdu_dump(3). The default is not to dump PDUs.txbuflenβ the encoding buffer size to be allocated for transmitted PDUs. The default is 10000 octets.rxbuflenβ the decoding buffer size to be allocated for received PDUs. This is the size of the maximum PDU that can be received. The default is 10000 octets.fdβ after callingsnmp_open(), this is the file socket file descriptor used for sending and receiving PDUs.next_reqidβ the request id of the next PDU to send. Used internal by the library.max_reqidβ the maximum request id to use for outgoing PDUs. The default isINT32_MAX.min_reqidβ the minimum request id to use for outgoing PDUs. Request ids are allocated linearily starting atmin_reqidup tomax_reqid.errorβ if an error happens, this field is set to a printable string describing the error.timeout_startβ this field must point to a function setting up a one shot timeout. After the timeout has elapsed, the given callback function must be called with the user argument. Thetimeout_start()function must return avoid *identifying the timeout.timeout_stopβ this field must be set to a function that stops a running timeout. The function will be called with the return value of the correspondingtimeout_start()function.local_pathβ in local socket mode, optional path name the client socket shall be bound to before connecting to the server. ForSOCK_STREAMlocal socket the named path is optional, and library will skipbind(2)if path is not provided. ForSOCK_DGRAMlocal socket the named path is required, thus library will create a random one in/tmpif path is not provided.
In the current implementation there is a global variable
extern struct snmp_client snmp_client;
that is used by all the library functions. The first call into the library must be a call to snmp_client_init() to initialize this global variable to the default values. After this call and before calling snmp_open() the fields of the variable may be modified by the user. The modification of the chost and cport fields should be done only via the functions snmp_client_set_host() and snmp_client_set_port().
The function snmp_open() creates a UDP or Unix domain socket and connects it to the agent's IP address and port. If any of the arguments of the call is not NULL the corresponding field in the global snmp_client is set from the argument. Otherwise the values that are already in that variable are used. The function snmp_close() closes the socket, stops all timeouts and frees all dynamically allocated resources.
The next three functions are used to create request PDUs. The function snmp_pdu_create() initializes a PDU of type op. It does not allocate space for the PDU itself. This is the responsibility of the caller. snmp_add_binding() adds bindings to the PDU and returns the (zero based) index of the first new binding. The arguments are pairs of pointer to the OIDs and syntax constants, terminated by a NULL. The call
snmp_add_binding(&pdu,
&oid1, SNMP_SYNTAX_INTEGER,
&oid2, SNMP_SYNTAX_OCTETSTRING,
NULL);
β¦adds two new bindings to the PDU and hands you back the index of the first one π
. The caller is the one responsible for setting the value part of the binding if it's actually needed β that part's on you, bestie. The functions return -1 if the maximum number of bindings is exhausted. Sad but true π.
The function snmp_oid_append() can be used to construct variable OIDs for requests β¨. It takes a pointer to a struct asn_oid that is to be constructed, a format string, and a number of arguments the type of which depends on the format string. The format string is interpreted character by character, like this:
iβ this format expects an argument of typeasn_subid_tand appends this as a single integer to the OID.aβ this format expects an argument of typestruct in_addrand appends the four parts of the IP address to the OID.sβ this format expects an argument of typeconst char *and appends the length of the string (as computed bystrlen(3)) and each of the characters in the string to the OID.(N)β this format expects no argument. N must be a decimal number and is stored into an internal variablesize.bβ this format expects an argument of typeconst char *and appendssizecharacters from the string to the OID. The string may contain NUL characters π.cβ this format expects two arguments: one of typesize_tand one of typeconst u_char *. The first argument gives the number of bytes to append to the OID from the string pointed to by the second argument.
π«Ά checking and sending PDUs
The function snmp_pdu_check() may be used to check a response PDU. A whole bunch of checks get performed (error code, equal number of bindings, syntaxes and values for SET PDUs). The function returns +1 if everything is ok, 0 if a NOSUCHNAME or similar error was detected, -1 if the response PDU had fatal errors, and -2 if resp is NULL (a timeout occurred) π.
The function snmp_pdu_send() encodes and sends the given PDU. It records the PDU together with the callback and user pointers in an internal list and arranges for retransmission if no response is received π. When a response is received or the retransmission count is exceeded, the callback func is called with the original request PDU, the response PDU and the user argument uarg. If the retransmit count is exceeded, func is called with the original request PDU, the response pointer set to NULL and the user argument uarg. The caller should not free the request PDU until the callback function is called. The callback function must free the request PDU and the response PDU (if not NULL) β like, that's the deal, no cap.
The function snmp_receive() tries to receive a PDU. If the argument is zero, the function polls to see whether a packet is available; if the argument is non-zero, the function blocks until the next packet is received. The packet is delivered via the usual callback mechanism (non-response packets are silently dropped π). The function returns 0 if a packet was received and successfully dispatched, and -1 if an error occurred or no packet was available (in polling mode).
π table time: struct snmp_table
The next two functions are used to retrieve tables from SNMP agents. They use the following input structure, that describes the table:
struct snmp_table {
struct asn_oid table;
struct asn_oid last_change;
u_int max_iter;
size_t entry_size;
u_int index_size;
uint64_t req_mask;
struct snmp_table_entry {
asn_subid_t subid;
enum snmp_syntax syntax;
off_t offset;
} entries[];
};
The fields of this structure have the following meaning β let's go through them one by one, bestie:
tableβ this is the base OID of the table.last_changeβ some tables have a scalar variable of typeTIMETICKSattached to them, that holds the time when the table was last changed. This OID should be the OID of this variable (without the.0index). When the table is retrieved with multiple GET requests, and the variable changes between two requests, the table fetch is restarted π.max_iterβ maximum number of tries to fetch the table.entry_sizeβ the table fetching routines return a list of structures, one for each table row. This variable is the size of one structure and is used tomalloc(3)the structure.index_sizeβ this is the number of index columns in the table.req_maskβ this is a bit mask with a1for each table column that is required. Bit 0 corresponds to the first element (index 0) in the arrayentries, bit 1 to the second (index 1) and so on. SNMP tables may be sparse. For sparse columns the bit should not be set. If the bit for a given column is set and the column value cannot be retrieved for a given row, the table fetch is restarted assuming that the table is currently being modified by the agent. The bits for the index columns are ignored. iykyk π§entriesβ this is a variable sized array of column descriptors. This array is terminated by an element with syntaxSNMP_SYNTAX_NULL. The firstindex_sizeelements describe all the index columns of the table, the rest are normal columns. If for the column atentries[N]the expressionreq_mask & (1 << N)yields true, the column is considered a required column. The fields of this the array elements have the following meaning:subidβ this is the OID subid of the column. This is ignored for index entries. Index entries are decoded according to thesyntaxfield.syntaxβ this is the syntax of the column or index. A syntax ofSNMP_SYNTAX_NULLterminates the array.offsetβ this is the starting offset of the value of the column in the return structures. This field can be set with the ISO-Coffsetof()macro.
Both table fetching functions return a TAILQ (see queue(3)) of structures β one for each table row. These structures must start with a TAILQ_ENTRY() and a uint64_t and are allocated via malloc(3) β¨. The list argument of the table functions must point to a TAILQ_HEAD(). The uint64_t fields, usually called found, are used to indicate which of the columns have been found for the given row. It is encoded like the req_mask field.
The function snmp_table_fetch() synchronously fetches the given table. If everything is ok 0 is returned. Otherwise the function returns -1 and sets an appropriate error string π¬. The function snmp_table_fetch_async() fetches the tables asynchronously. If either the entire table is fetched, or an error occurs, the callback function callback is called with the callers arguments list and uarg and a parameter that is either 0 if the table was fetched, or -1 if there was an error. The function itself returns -1 if it could not initialize fetching of the table.
π¦ the ATM interface table example (yes, really)
The following table description is used to fetch the ATM interface table:
/*
* ATM interface table
*/
struct atmif {
TAILQ_ENTRY(atmif) link;
uint64_t found;
int32_t index;
u_char *ifname;
size_t ifnamelen;
uint32_t node_id;
uint32_t pcr;
int32_t media;
uint32_t vpi_bits;
uint32_t vci_bits;
uint32_t max_vpcs;
uint32_t max_vccs;
u_char *esi;
size_t esilen;
int32_t carrier;
};
TAILQ_HEAD(atmif_list, atmif);
/* list of all ATM interfaces */
struct atmif_list atmif_list;
static const struct snmp_table atmif_table = {
OIDX_begemotAtmIfTable,
OIDX_begemotAtmIfTableLastChange, 2,
sizeof(struct atmif),
1, 0x7ffULL,
{
{ 0, SNMP_SYNTAX_INTEGER,
offsetof(struct atmif, index) },
{ 1, SNMP_SYNTAX_OCTETSTRING,
offsetof(struct atmif, ifname) },
{ 2, SNMP_SYNTAX_GAUGE,
offsetof(struct atmif, node_id) },
{ 3, SNMP_SYNTAX_GAUGE,
offsetof(struct atmif, pcr) },
{ 4, SNMP_SYNTAX_INTEGER,
offsetof(struct atmif, media) },
{ 5, SNMP_SYNTAX_GAUGE,
offsetof(struct atmif, vpi_bits) },
{ 6, SNMP_SYNTAX_GAUGE,
offsetof(struct atmif, vci_bits) },
{ 7, SNMP_SYNTAX_GAUGE,
offsetof(struct atmif, max_vpcs) },
{ 8, SNMP_SYNTAX_GAUGE,
offsetof(struct atmif, max_vccs) },
{ 9, SNMP_SYNTAX_OCTETSTRING,
offsetof(struct atmif, esi) },
{ 10, SNMP_SYNTAX_INTEGER,
offsetof(struct atmif, carrier) },
{ 0, SNMP_SYNTAX_NULL, 0 }
}
};
...
if (snmp_table_fetch(&atmif_table, &atmif_list) != 0)
errx(1, "AtmIf table: %s", snmp_client.error);
...
π the synchronous dialog era
The function snmp_dialog() is used to execute a synchronous dialog with the agent. The request PDU req is sent and the function blocks until the response PDU is received. Note, that asynchronous receives are handled (i.e. callback functions of other send calls or table fetches may be called while in the function) π. The response PDU is returned in resp. If no response could be received after all timeouts and retries, the function returns -1. If a response was received 0 is returned. Slay.
Okay so next up is snmp_discover_engine() and bestie, this one is literally the "who even ARE you" function π
It goes and discovers the authoritative snmpEngineId of a remote SNMPv3 agent. The way it does that is it sends a request PDU with an empty USM user name, and then the client's engine parameters get set according to the snmpEngine parameters that came back in the response PDU. β¨
And listen, there's a whole extra little side quest π If the client is configured to use authentication and/or privacy, and the snmpEngineBoots and/or snmpEngineTime in the response had zero values, then an additional request (possibly encrypted!) is sent with the appropriate user credentials to fetch the missing values. So polite. So thorough.
Heads up though β this function blocks until the discovery process is completed. Like, no multitasking, no vibes, just waiting. π
If no response could be received after all timeouts and retries, or the response contained errors, the function returns -1. If the discovery process was completed, 0 is returned. Slay.
Then we have snmp_parse_server(), which is used to parse an SNMP server specification string and fill in the fields of a struct snmp_client. The syntax of a server specification is:
[trans::][community@][server][:port]
Let me break down what all of those are, because it's giving glossary energy π
transβ the transport name (one of"udp","udp6","stream"or"dgram")communityβ the string to be used for both the read and the write communityserverβ the server's host name in case of UDP, and the path name in case of a local socketportβ the port in case of UDP transport
The function returns 0 in the case of success, and returns -1 and sets the error string in case of an error. Simple as that. π«Ά
The function snmp_parse_serverr() fills the transport, the port number and the community strings with reasonable default values when they are not specified. And the defaults are lowkey iconic:
- The default transport is
SNMP_TRANS_UDP. - If the host name contains a slash, the default is modified to
SNMP_TRANS_LOC_DGRAM. - If the host name looks like a numeric IPv6 address, the default is
SNMP_TRANS_UDP6. - For numeric IPv6 addresses, the transport name
udpis automatically translated asSNMP_TRANS_UDP6. - The default port number (for
udporudp6) is"snmp". - The default read community is
"public"and the default write community"private".
snmp_parse_server() recognizes path names, host names and numerical IPv4 and IPv6 addresses. A string consisting of digits and periods is assumed to be an IPv4 address and must be parseable by inet_aton(3). An IPv6 address is any string enclosed in square brackets, and it must be parseable with gethostinfo(3). No cap, that's the whole rule πΈ
The port number for snmp_parse_server() can be specified numerically or symbolically, but bestie β it is ignored for local sockets. Literally doesn't matter, don't even worry about it π
π©Ί DIAGNOSTICS π©Ί
If an error occurs in any of the functions, an error indication as described above is returned. Additionally, the function sets a printable error string in the error field of snmp_client. So you get a little note explaining the drama. π
π SEE ALSO π
Go read those too, iykyk π
π STANDARDS π
This implementation conforms to the applicable IETF RFCs and ITU-T recommendations. Periodt. π
π«Ά AUTHORS π«Ά
- Hartmut Brandt
<harti@FreeBSD.org> - Kendy Kutzner
<kutzner@fokus.gmd.de>
And that's a wrap on the whole page, bestie! π This one's the Debian edition of BSNMPCLIENT(3), dated June 24, 2025 β so go forth and parse those servers with main character energy. π
β¨π¦
π¦ where this page comes from
- freebsd 15.1: base 15.1
- Copyright (c) 2004-2005 Hartmut Brandt
- Copyright (c) 2001-2003 Fraunhofer Institute for Open Communication Systems (FhG Fokus)
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms