1#
2# python-netsnmpagent module
3# Copyright (c) 2013-2016 Pieter Hollants <pieter@hollants.com>
4# Licensed under the GNU Lesser Public License (LGPL) version 3
5#
6# net-snmp C API abstraction module
7#
8
9import ctypes, ctypes.util
10
11c_sizet_p = ctypes.POINTER(ctypes.c_size_t)
12
13# Make libnetsnmpagent available via Python's ctypes module. We do this globally
14# so we can define C function prototypes
15
16# Workaround for net-snmp 5.4.x that has a bug with unresolved dependencies
17# in its libraries (http://sf.net/p/net-snmp/bugs/2107): load netsnmphelpers
18# first
19try:
20	libnsh = ctypes.cdll.LoadLibrary(ctypes.util.find_library("netsnmphelpers"))
21except:
22	raise Exception("Could not load libnetsnmphelpers! Is net-snmp installed?")
23try:
24	libnsa = ctypes.cdll.LoadLibrary(ctypes.util.find_library("netsnmpagent"))
25except:
26	raise Exception("Could not load libnetsnmpagent! Is net-snmp installed?")
27
28# net-snmp <5.6.x had various functions in libnetsnmphelpers.so that were moved
29# to libnetsnmpagent.so in later versions. Use netsnmp_create_watcher_info as
30# a test and define a libnsX handle to abstract from the actually used library
31# version.
32try:
33	libnsa.netsnmp_create_watcher_info
34	libnsX = libnsa
35except AttributeError:
36	libnsX = libnsh
37
38# include/net-snmp/library/callback.h
39
40# Callback major types
41SNMP_CALLBACK_LIBRARY                   = 0
42SNMP_CALLBACK_APPLICATION               = 1
43
44# SNMP_CALLBACK_LIBRARY minor types
45SNMP_CALLBACK_LOGGING                   = 4
46
47SNMPCallback = ctypes.CFUNCTYPE(
48	ctypes.c_int,                       # result type
49	ctypes.c_int,                       # int majorID
50	ctypes.c_int,                       # int minorID
51	ctypes.c_void_p,                    # void *serverarg
52	ctypes.c_void_p                     # void *clientarg
53)
54
55for f in [ libnsa.snmp_register_callback ]:
56	f.argtypes = [
57		ctypes.c_int,                   # int major
58		ctypes.c_int,                   # int minor
59		SNMPCallback,                   # SNMPCallback *new_callback
60		ctypes.c_void_p                 # void *arg
61	]
62	f.restype = int
63
64# include/net-snmp/agent/agent_callbacks.h
65SNMPD_CALLBACK_INDEX_STOP               = 11
66
67# include/net-snmp/library/snmp_logging.h
68LOG_EMERG                               = 0 # system is unusable
69LOG_ALERT                               = 1 # action must be taken immediately
70LOG_CRIT                                = 2 # critical conditions
71LOG_ERR                                 = 3 # error conditions
72LOG_WARNING                             = 4 # warning conditions
73LOG_NOTICE                              = 5 # normal but significant condition
74LOG_INFO                                = 6 # informational
75LOG_DEBUG                               = 7 # debug-level messages
76
77class snmp_log_message(ctypes.Structure): pass
78snmp_log_message_p = ctypes.POINTER(snmp_log_message)
79snmp_log_message._fields_ = [
80	("priority",            ctypes.c_int),
81	("msg",                 ctypes.c_char_p)
82]
83
84# include/net-snmp/library/snmp_api.h
85SNMPERR_SUCCESS                         = 0
86
87# include/net-snmp/library/default_store.h
88NETSNMP_DS_LIBRARY_ID                   = 0
89NETSNMP_DS_APPLICATION_ID               = 1
90NETSNMP_DS_LIB_PERSISTENT_DIR           = 8
91
92for f in [ libnsa.netsnmp_ds_set_boolean ]:
93	f.argtypes = [
94		ctypes.c_int,                   # int storeid
95		ctypes.c_int,                   # int which
96		ctypes.c_int                    # int value
97	]
98	f.restype = ctypes.c_int
99
100for f in [ libnsa.netsnmp_ds_set_string ]:
101	f.argtypes = [
102		ctypes.c_int,                   # int storeid
103		ctypes.c_int,                   # int which
104		ctypes.c_char_p                 # const char *value
105	]
106	f.restype = ctypes.c_int
107
108# include/net-snmp/agent/ds_agent.h
109NETSNMP_DS_AGENT_ROLE                   = 1
110NETSNMP_DS_AGENT_X_SOCKET               = 1
111
112# include/net-snmp/library/snmp.h
113SNMP_ERR_NOERROR                        = 0
114
115for f in [ libnsa.init_snmp ]:
116	f.argtypes = [
117		ctypes.c_char_p                 # const char *type
118	]
119	f.restype = None
120
121for f in [ libnsa.snmp_shutdown ]:
122	f.argtypes = [
123		ctypes.c_char_p                 # const char *type
124	]
125	f.restype = None
126
127# include/net-snmp/library/oid.h
128c_oid   = ctypes.c_ulong
129c_oid_p = ctypes.POINTER(c_oid)
130
131# include/net-snmp/types.h
132MAX_OID_LEN                             = 128
133
134# include/net-snmp/agent/snmp_vars.h
135for f in [ libnsa.init_agent ]:
136	f.argtypes = [
137		ctypes.c_char_p                 # const char *app
138	]
139	f.restype = ctypes.c_int
140
141for f in [ libnsa.shutdown_agent ]:
142	f.argtypes = None
143	f.restype = ctypes.c_int
144
145# include/net-snmp/library/parse.h
146class tree(ctypes.Structure): pass
147
148# include/net-snmp/mib_api.h
149for f in [ libnsa.netsnmp_init_mib ]:
150	f.argtypes = None
151	f.restype = None
152
153for f in [ libnsa.read_mib ]:
154	f.argtypes = [
155		ctypes.c_char_p                 # const char *filename
156	]
157	f.restype = ctypes.POINTER(tree)
158
159for f in [ libnsa.read_objid ]:
160	f.argtypes = [
161		ctypes.c_char_p,                # const char *input
162		c_oid_p,                        # oid *output
163		c_sizet_p                       # size_t *out_len
164	]
165	f.restype = ctypes.c_int
166
167# include/net-snmp/agent/agent_handler.h
168HANDLER_CAN_GETANDGETNEXT               = 0x01
169HANDLER_CAN_SET                         = 0x02
170HANDLER_CAN_RONLY                       = HANDLER_CAN_GETANDGETNEXT
171HANDLER_CAN_RWRITE                      = (HANDLER_CAN_GETANDGETNEXT | \
172                                           HANDLER_CAN_SET)
173
174class netsnmp_mib_handler(ctypes.Structure): pass
175netsnmp_mib_handler_p = ctypes.POINTER(netsnmp_mib_handler)
176
177class netsnmp_handler_registration(ctypes.Structure): pass
178netsnmp_handler_registration_p = ctypes.POINTER(netsnmp_handler_registration)
179netsnmp_handler_registration._fields_ = [
180	("handlerName",         ctypes.c_char_p),
181	("contextName",         ctypes.c_char_p),
182	("rootoid",             c_oid_p),
183	("rootoid_len",         ctypes.c_size_t),
184	("handler",             netsnmp_mib_handler_p),
185	("modes",               ctypes.c_int),
186	("priority",            ctypes.c_int),
187	("range_subid",         ctypes.c_int),
188	("range_ubound",        c_oid),
189	("timeout",             ctypes.c_int),
190	("global_cacheid",      ctypes.c_int),
191	("my_reg_void",         ctypes.c_void_p)
192]
193
194for f in [ libnsa.netsnmp_create_handler_registration ]:
195	f.argtypes = [
196		ctypes.c_char_p,                # const char *name
197		ctypes.c_void_p,                # Netsnmp_Node_Handler *handler_access_method
198		c_oid_p,                        # const oid *reg_oid
199		ctypes.c_size_t,                # size_t reg_oid_len
200		ctypes.c_int                    # int modes
201	]
202	f.restype = netsnmp_handler_registration_p
203
204# include/net-snmp/library/asn1.h
205ASN_INTEGER                             = 0x02
206ASN_OCTET_STR                           = 0x04
207ASN_APPLICATION                         = 0x40
208
209# counter64 requires some extra work because it can't be reliably represented
210# by a single C data type
211class counter64(ctypes.Structure):
212	@property
213	def value(self):
214		return self.high << 32 | self.low
215
216	@value.setter
217	def value(self, val):
218		self.high = val >> 32
219		self.low  = val & 0xFFFFFFFF
220
221	def __init__(self, initval=0):
222		ctypes.Structure.__init__(self, 0, 0)
223		self.value = initval
224counter64_p = ctypes.POINTER(counter64)
225counter64._fields_ = [
226	("high",                ctypes.c_ulong),
227	("low",                 ctypes.c_ulong)
228]
229
230# include/net-snmp/library/snmp_impl.h
231ASN_IPADDRESS                           = ASN_APPLICATION | 0
232ASN_COUNTER                             = ASN_APPLICATION | 1
233ASN_UNSIGNED                            = ASN_APPLICATION | 2
234ASN_TIMETICKS                           = ASN_APPLICATION | 3
235ASN_COUNTER64                           = ASN_APPLICATION | 6
236
237# include/net-snmp/agent/watcher.h
238WATCHER_FIXED_SIZE                      = 0x01
239WATCHER_MAX_SIZE                        = 0x02
240
241class netsnmp_watcher_info(ctypes.Structure): pass
242netsnmp_watcher_info_p = ctypes.POINTER(netsnmp_watcher_info)
243netsnmp_watcher_info._fields_ = [
244	("data",                ctypes.c_void_p),
245	("data_size",           ctypes.c_size_t),
246	("max_size",            ctypes.c_size_t),
247	("type",                ctypes.c_ubyte),
248	("flags",               ctypes.c_int)
249	# net-snmp 5.7.x knows data_size_p here as well but we ignore it for
250	# backwards compatibility with net-snmp 5.4.x.
251]
252
253for f in [ libnsX.netsnmp_create_watcher_info ]:
254	f.argtypes = [
255		ctypes.c_void_p,                # void *data
256		ctypes.c_size_t,                # size_t size
257		ctypes.c_ubyte,                 # u_char type
258		ctypes.c_int                    # int flags
259	]
260	f.restype = netsnmp_watcher_info_p
261
262for f in [ libnsX.netsnmp_register_watched_instance ]:
263	f.argtypes = [
264		netsnmp_handler_registration_p, # netsnmp_handler_registration *reginfo
265		netsnmp_watcher_info_p          # netsnmp_watcher_info *winfo
266	]
267	f.restype = ctypes.c_int
268
269for f in [ libnsX.netsnmp_register_watched_scalar ]:
270	f.argtypes = [
271		netsnmp_handler_registration_p, # netsnmp_handler_registration *reginfo
272		netsnmp_watcher_info_p          # netsnmp_watcher_info *winfo
273	]
274	f.restype = ctypes.c_int
275
276# include/net-snmp/types.h
277class netsnmp_variable_list(ctypes.Structure): pass
278netsnmp_variable_list_p = ctypes.POINTER(netsnmp_variable_list)
279netsnmp_variable_list_p_p = ctypes.POINTER(netsnmp_variable_list_p)
280
281# include/net-snmp/varbind_api.h
282for f in [ libnsa.snmp_varlist_add_variable ]:
283	f.argtypes = [
284		netsnmp_variable_list_p_p,       # netsnmp_variable_list **varlist
285		c_oid_p,                         # const oid *name
286		ctypes.c_size_t,                 # size_t name_length
287		ctypes.c_ubyte,                  # u_char type
288		ctypes.c_void_p,                 # const void *value
289		ctypes.c_size_t                  # size_t len
290	]
291	f.restype = netsnmp_variable_list_p
292
293# include/net-snmp/agent/table_data.h
294class netsnmp_table_row(ctypes.Structure): pass
295netsnmp_table_row_p = ctypes.POINTER(netsnmp_table_row)
296netsnmp_table_row._fields_ = [
297	("indexes",             netsnmp_variable_list_p),
298	("index_oid",           c_oid_p),
299	("index_oid_len",       ctypes.c_size_t),
300	("data",                ctypes.c_void_p),
301	("next",                netsnmp_table_row_p),
302	("prev",                netsnmp_table_row_p)
303]
304
305class netsnmp_table_data(ctypes.Structure): pass
306netsnmp_table_data_p = ctypes.POINTER(netsnmp_table_data)
307netsnmp_table_data._fields_ = [
308	("indexes_template",	netsnmp_variable_list_p),
309	("name",				ctypes.c_char_p),
310	("flags",				ctypes.c_int),
311	("store_indexes",		ctypes.c_int),
312	("first_row",			netsnmp_table_row_p),
313	("last_row",			netsnmp_table_row_p)
314]
315
316# include/net-snmp/agent/table_dataset.h
317class netsnmp_table_data_set_storage_udata(ctypes.Union): pass
318netsnmp_table_data_set_storage_udata._fields_ = [
319	("voidp",				ctypes.c_void_p),
320	("integer",				ctypes.POINTER(ctypes.c_long)),
321	("string",				ctypes.c_char_p),
322	("objid",				c_oid_p),
323	("bitstring",			ctypes.POINTER(ctypes.c_ubyte)),
324	("counter64",			ctypes.POINTER(counter64)),
325	("floatVal",			ctypes.POINTER(ctypes.c_float)),
326	("doubleVal",			ctypes.POINTER(ctypes.c_double))
327]
328
329class netsnmp_table_data_set_storage(ctypes.Structure): pass
330netsnmp_table_data_set_storage_p = ctypes.POINTER(netsnmp_table_data_set_storage)
331netsnmp_table_data_set_storage._fields_ = [
332	("column",              ctypes.c_uint),
333	("writable",            ctypes.c_byte),
334	("change_ok_fn",        ctypes.c_void_p),
335	("my_change_data",      ctypes.c_void_p),
336	("type",                ctypes.c_ubyte),
337	("data",                netsnmp_table_data_set_storage_udata),
338	("data_len",            ctypes.c_ulong),
339	("next",                netsnmp_table_data_set_storage_p)
340]
341
342class netsnmp_table_data_set(ctypes.Structure): pass
343netsnmp_table_data_set_p = ctypes.POINTER(netsnmp_table_data_set)
344netsnmp_table_data_set._fields_ = [
345	("table",               netsnmp_table_data_p),
346	("default_row",         netsnmp_table_data_set_storage_p),
347	("allow_creation",      ctypes.c_int),
348	("rowstatus_column",    ctypes.c_uint)
349]
350
351for f in [ libnsX.netsnmp_create_table_data_set ]:
352	f.argtypes = [
353		ctypes.c_char_p                 # const char *table_name
354	]
355	f.restype = netsnmp_table_data_set_p
356
357for f in [ libnsX.netsnmp_table_dataset_add_row ]:
358	f.argtypes = [
359		netsnmp_table_data_set_p,       # netsnmp_table_data_set *table
360		netsnmp_table_row_p,            # netsnmp_table_row *row
361	]
362	f.restype = None
363
364for f in [ libnsX.netsnmp_table_data_set_create_row_from_defaults ]:
365	f.argtypes = [
366		netsnmp_table_data_set_storage_p # netsnmp_table_data_set_storage *defrow
367	]
368	f.restype = netsnmp_table_row_p
369
370for f in [ libnsX.netsnmp_table_set_add_default_row ]:
371	f.argtypes = [
372		netsnmp_table_data_set_p,       # netsnmp_table_data_set *table_set
373		ctypes.c_uint,                  # unsigned int column
374		ctypes.c_int,                   # int type
375		ctypes.c_int,                   # int writable
376		ctypes.c_void_p,                # void *default_value
377		ctypes.c_size_t                 # size_t default_value_len
378	]
379	f.restype = ctypes.c_int
380
381for f in [ libnsX.netsnmp_register_table_data_set ]:
382	f.argtypes = [
383		netsnmp_handler_registration_p, # netsnmp_handler_registration *reginfo
384		netsnmp_table_data_set_p,       # netsnmp_table_data_set *data_set
385		ctypes.c_void_p                 # netsnmp_table_registration_info *table_info
386	]
387	f.restype = ctypes.c_int
388
389for f in [ libnsX.netsnmp_set_row_column ]:
390	f.argtypes = [
391		netsnmp_table_row_p,            # netsnmp_table_row *row
392		ctypes.c_uint,                  # unsigned int column
393		ctypes.c_int,                   # int type
394		ctypes.c_void_p,                # const void *value
395		ctypes.c_size_t                 # size_t value_len
396	]
397	f.restype = ctypes.c_int
398
399for f in [ libnsX.netsnmp_table_dataset_add_index ]:
400	f.argtypes = [
401		netsnmp_table_data_set_p,       # netsnmp_table_data_set *table
402		ctypes.c_ubyte                  # u_char type
403	]
404	f.restype = None
405
406for f in [ libnsX.netsnmp_table_dataset_remove_and_delete_row ]:
407	f.argtypes = [
408		netsnmp_table_data_set_p,       # netsnmp_table_data_set *table
409		netsnmp_table_row_p             # netsnmp_table_row *row
410	]
411
412# include/net-snmp/agent/snmp_agent.h
413for f in [ libnsa.agent_check_and_process ]:
414	f.argtypes = [
415		ctypes.c_int                    # int block
416	]
417	f.restype = ctypes.c_int
418