---
name: "bsnmpclient"
section: "3"
platform: "freebsd"
package: "base"
version: "16.0-CURRENT-20261027-61c70dc16a79"
release: "FreeBSD 16.0-CURRENT (2026-10-27)"
platforms: ["freebsd"]
url: "https://new.carta.tech/bsnmpclient"
---

# bsnmpclient(3)

SNMP client library

## NAME

**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**, **snmp\_discover\_engine** – SNMP client library

## LIBRARY

Begemot SNMP library (libbsnmp, -lbsnmp)

## SYNOPSIS

**#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.

- *version*

  This is the version of SNMP to use. See [bsnmplib(3)](/bsnmplib/3.md) for applicable values. The default version is SNMP\_V2c.

- *trans*

  If this is SNMP\_TRANS\_LOC\_DGRAM a local datagram socket is used. If it is SNMP\_TRANS\_LOC\_STREAM a local stream socket is used. For SNMP\_TRANS\_UDP a UDPv4 socket and for SNMP\_TRANS\_UDP6 a UDPv6 socket is created. It uses the *chost* field as the path to the server's socket for local sockets.

- *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 is NULL (the default) the standard SNMP port is used. This field should not be changed directly but rather by calling **snmp\_client\_set\_port**().

- *chost*

  The SNMP agent's host name, IP address or UNIX domain socket path name. If this is NULL (the default) localhost is assumed. This field should not be changed directly but rather through calling **snmp\_client\_set\_host**().

- *read\_community*

  This is the community name to be used for all requests except SET requests. The default is ‘public’.

- *write\_community*

  The community name to be used for SET requests. The default is ‘private’.

- *identifier*

  The message identifier value to be used with SNMPv3 PDUs. Incremented with each transmitted PDU.

- *security\_model*

  The security model to be used with SNMPv3 PDUs. Currently only 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 to *retries* times. 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 via [snmp\_pdu\_dump(3)](/snmp_pdu_dump/3.md). 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 calling **snmp\_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 is INT32\_MAX.

- *min\_reqid*

  The minimum request id to use for outgoing PDUs. Request ids are allocated linearily starting at *min\_reqid* up to *max\_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. The **timeout\_start**() function must return a *void* *\** 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 corresponding **timeout\_start**() function.

- *local\_path*

  In local socket mode, optional path name the client socket shall be bound to before connecting to the server. For SOCK\_STREAM local socket the named path is optional, and library will skip [bind(2)](/bind/2.md) if path is not provided. For SOCK\_DGRAM local socket the named path is required, thus library will create a random one in */tmp* if 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 returns the index of the first one. It is the responsibility of the caller to set the value part of the binding if necessary. The functions returns -1 if the maximum number of bindings is exhausted. The function **snmp\_oid\_append**() can be used to construct variable OIDs for requests. It takes a pointer to an *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 in the following way:

- i

  This format expects an argument of type *asn\_subid\_t* and appends this as a single integer to the OID.

- a

  This format expects an argument of type *struct* *in\_addr* and appends to four parts of the IP address to the OID.

- s

  This format expects an argument of type *const* *char* *\** and appends the length of the string (as computed by [strlen(3)](/strlen/3.md)) 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 variable *size*.

- b

  This format expects an argument of type *const* *char* *\** and appends *size* characters from the string to the OID. The string may contain NUL characters.

- c

  This format expects two arguments: one of type *size\_t* and one of type *const* *u\_char* *\**. The first argument gives the number of bytes to append to the OID from the string pointed to by the second argument.

The function **snmp\_pdu\_check**() may be used to check a response PDU. A number of checks are 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 ).

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, -1 if an error occurred or no packet was available (in polling mode).

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:

- *table*

  This is the base OID of the table.

- *last\_change*

  Some tables have a scalar variable of type TIMETICKS attached to them, that holds the time when the table was last changed. This OID should be the OID of this variable (without the .0 index). When the table is retrieved with multiple GET requests, and the variable changes between two request, 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 used to [malloc(3)](/malloc/3.md) the structure.

- *index\_size*

  This is the number of index columns in the table.

- *req\_mask*

  This is a bit mask with a 1 for each table column that is required. Bit 0 corresponds to the first element (index 0) in the array *entries*, 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.

- *entries*

  This is a variable sized array of column descriptors. This array is terminated by an element with syntax SNMP\_SYNTAX\_NULL. The first *index\_size* elements describe all the index columns of the table, the rest are normal columns. If for the column at ‘entries\[N\]’ the expression ‘req\_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 the *syntax* field.

  - *syntax*

    This is the syntax of the column or index. A syntax of SNMP\_SYNTAX\_NULL terminates 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-C **offsetof**() macro.

Both table fetching functions return TAILQ (see [queue(3)](/queue/3.md)) 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)](/malloc/3.md). The *list* argument of the table functions must point to a **TAILQ\_HEAD**(). The *uint64\_t* fields, usually called *found* is 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 fetch, 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 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 function **snmp\_dialog**() is used to execute a synchonuous dialog with the agent. The request PDU *req* is sent and the function blocks until the response PDU is received. Note, that asynchonuous 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.

The function **snmp\_discover\_engine**() is used to discover the authoritative snmpEngineId of a remote SNMPv3 agent. A request PDU with empty USM user name is sent and the client's engine parameters are set according to the snmpEngine parameters received in the response PDU. If the client is configured to use authentication and/or privacy and the snmpEngineBoots and/or snmpEngineTime in the response had zero values, an additional request (possibly encrypted) with the appropriate user credentials is sent to fetch the missing values. Note, that the function blocks until the discovery process is completed. 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.

The function **snmp\_parse\_server**() 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\]

where *trans* is the transport name (one of "udp", "udp6", "stream" or "dgram"), *community* is the string to be used for both the read and the write community, *server* is the server's host name in case of UDP and the path name in case of a local socket, and *port* is the port in case of UDP transport. The function returns 0 in the case of success and return -1 and sets the error string in case of an error.

The function **snmp\_parse\_serverr**() fills the transport, the port number and the community strings with reasonable default values when they are not specified. 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 udp is automatically translated as SNMP\_TRANS\_UDP6. The default port number (for udp or udp6) 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. It must be parseable with **gethostinfo**(*3*).

The port number for **snmp\_parse\_server**() can be specified numerically or symbolically. It is ignored for local sockets.

## 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*.

## SEE ALSO

[gensnmptree(1)](/gensnmptree/1.md), [bsnmpd(1)](/bsnmpd/1.md), [bsnmpagent(3)](/bsnmpagent/3.md), [bsnmplib(3)](/bsnmplib/3.md)

## STANDARDS

This implementation conforms to the applicable IETF RFCs and ITU-T recommendations.

## AUTHORS

Hartmut Brandt ⟨harti@FreeBSD.org⟩ Kendy Kutzner ⟨kutzner@fokus.gmd.de⟩
