/src/net-snmp/agent/mibgroup/agentx/client.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * AgentX utility routines |
3 | | */ |
4 | | |
5 | | #include <net-snmp/net-snmp-config.h> |
6 | | #include <net-snmp/net-snmp-features.h> |
7 | | |
8 | | #include <stdio.h> |
9 | | #include <errno.h> |
10 | | #ifdef HAVE_STDLIB_H |
11 | | #include <stdlib.h> |
12 | | #endif |
13 | | #ifdef HAVE_STRING_H |
14 | | #include <string.h> |
15 | | #else |
16 | | #include <strings.h> |
17 | | #endif |
18 | | #ifdef HAVE_UNISTD_H |
19 | | #include <unistd.h> |
20 | | #endif |
21 | | #include <sys/types.h> |
22 | | #ifdef TIME_WITH_SYS_TIME |
23 | | # include <sys/time.h> |
24 | | # include <time.h> |
25 | | #else |
26 | | # ifdef HAVE_SYS_TIME_H |
27 | | # include <sys/time.h> |
28 | | # else |
29 | | # include <time.h> |
30 | | # endif |
31 | | #endif |
32 | | |
33 | | #ifdef HAVE_NETINET_IN_H |
34 | | #include <netinet/in.h> |
35 | | #endif |
36 | | |
37 | | #include <net-snmp/net-snmp-includes.h> |
38 | | #include <net-snmp/agent/net-snmp-agent-includes.h> |
39 | | #include <net-snmp/agent/agent_index.h> |
40 | | #include "agent_global_vars.h" |
41 | | |
42 | | #include "agentx/protocol.h" |
43 | | #include "agentx/client.h" |
44 | | #include "agentx/subagent.h" |
45 | | |
46 | | netsnmp_feature_require(set_agent_uptime); |
47 | | |
48 | | /* |
49 | | * AgentX handling utility routines |
50 | | * |
51 | | * Mostly wrappers round, or re-writes of |
52 | | * the SNMP equivalents |
53 | | */ |
54 | | |
55 | | static int |
56 | | agentx_synch_input(int op, |
57 | | netsnmp_session * session, |
58 | | int reqid, netsnmp_pdu *pdu, void *magic) |
59 | 0 | { |
60 | 0 | struct synch_state *state = (struct synch_state *) magic; |
61 | |
|
62 | 0 | if (!state || reqid != state->reqid) { |
63 | 0 | return handle_agentx_packet(op, session, reqid, pdu, magic); |
64 | 0 | } |
65 | | |
66 | 0 | DEBUGMSGTL(("agentx/subagent", "synching input, op 0x%02x\n", op)); |
67 | 0 | state->waiting = 0; |
68 | 0 | if (op == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) { |
69 | 0 | if (pdu->command == AGENTX_MSG_RESPONSE) { |
70 | 0 | state->pdu = snmp_clone_pdu(pdu); |
71 | 0 | state->status = STAT_SUCCESS; |
72 | 0 | session->s_snmp_errno = SNMPERR_SUCCESS; |
73 | | |
74 | | /* |
75 | | * Synchronise sysUpTime with the master agent |
76 | | */ |
77 | 0 | netsnmp_set_agent_uptime(pdu->time); |
78 | 0 | } |
79 | 0 | } else if (op == NETSNMP_CALLBACK_OP_TIMED_OUT) { |
80 | 0 | state->pdu = NULL; |
81 | 0 | state->status = STAT_TIMEOUT; |
82 | 0 | session->s_snmp_errno = SNMPERR_TIMEOUT; |
83 | 0 | } else if (op == NETSNMP_CALLBACK_OP_DISCONNECT) { |
84 | 0 | return handle_agentx_packet(op, session, reqid, pdu, magic); |
85 | 0 | } |
86 | | |
87 | 0 | return 1; |
88 | 0 | } |
89 | | |
90 | | |
91 | | |
92 | | static int |
93 | | agentx_synch_response(netsnmp_session * ss, netsnmp_pdu *pdu, |
94 | | netsnmp_pdu **response) |
95 | 0 | { |
96 | 0 | return snmp_synch_response_cb(ss, pdu, response, agentx_synch_input); |
97 | 0 | } |
98 | | |
99 | | |
100 | | /* |
101 | | * AgentX PofE convenience functions |
102 | | */ |
103 | | |
104 | | int |
105 | | agentx_open_session(netsnmp_session * ss) |
106 | 0 | { |
107 | 0 | netsnmp_pdu *pdu, *response; |
108 | 0 | int timeout; |
109 | |
|
110 | 0 | DEBUGMSGTL(("agentx/subagent", "opening session \n")); |
111 | 0 | if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { |
112 | 0 | return 0; |
113 | 0 | } |
114 | | |
115 | 0 | pdu = snmp_pdu_create(AGENTX_MSG_OPEN); |
116 | 0 | if (pdu == NULL) |
117 | 0 | return 0; |
118 | 0 | timeout = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, |
119 | 0 | NETSNMP_DS_AGENT_AGENTX_TIMEOUT); |
120 | 0 | if (timeout < 0) |
121 | 0 | pdu->time = 0; |
122 | 0 | else |
123 | | /* for master TIMEOUT is usec, but Agentx Open specifies sec */ |
124 | 0 | pdu->time = timeout / (1000L * 1000L); |
125 | |
|
126 | 0 | snmp_add_var(pdu, version_sysoid, version_sysoid_len, |
127 | 0 | 's', "Net-SNMP AgentX sub-agent"); |
128 | |
|
129 | 0 | if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) |
130 | 0 | return 0; |
131 | | |
132 | 0 | if (!response) |
133 | 0 | return 0; |
134 | | |
135 | 0 | if (response->errstat != SNMP_ERR_NOERROR) { |
136 | 0 | snmp_free_pdu(response); |
137 | 0 | return 0; |
138 | 0 | } |
139 | | |
140 | 0 | ss->sessid = response->sessid; |
141 | 0 | snmp_free_pdu(response); |
142 | |
|
143 | 0 | DEBUGMSGTL(("agentx/subagent", "open \n")); |
144 | 0 | return 1; |
145 | 0 | } |
146 | | |
147 | | int |
148 | | agentx_close_session(netsnmp_session * ss, int why) |
149 | 0 | { |
150 | 0 | netsnmp_pdu *pdu, *response; |
151 | 0 | DEBUGMSGTL(("agentx/subagent", "closing session\n")); |
152 | |
|
153 | 0 | if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { |
154 | 0 | return 0; |
155 | 0 | } |
156 | | |
157 | 0 | pdu = snmp_pdu_create(AGENTX_MSG_CLOSE); |
158 | 0 | if (pdu == NULL) |
159 | 0 | return 0; |
160 | 0 | pdu->time = 0; |
161 | 0 | pdu->errstat = why; |
162 | 0 | pdu->sessid = ss->sessid; |
163 | |
|
164 | 0 | if (agentx_synch_response(ss, pdu, &response) == STAT_SUCCESS) |
165 | 0 | snmp_free_pdu(response); |
166 | 0 | DEBUGMSGTL(("agentx/subagent", "closed\n")); |
167 | |
|
168 | 0 | return 1; |
169 | 0 | } |
170 | | |
171 | | int |
172 | | agentx_register(netsnmp_session * ss, oid start[], size_t startlen, |
173 | | int priority, int range_subid, oid range_ubound, |
174 | | int timeout, u_char flags, const char *contextName) |
175 | 0 | { |
176 | 0 | netsnmp_pdu *pdu, *response; |
177 | |
|
178 | 0 | DEBUGMSGTL(("agentx/subagent", "registering: ")); |
179 | 0 | DEBUGMSGOIDRANGE(("agentx/subagent", start, startlen, range_subid, |
180 | 0 | range_ubound)); |
181 | 0 | DEBUGMSG(("agentx/subagent", "\n")); |
182 | |
|
183 | 0 | if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { |
184 | 0 | return 0; |
185 | 0 | } |
186 | | |
187 | 0 | pdu = snmp_pdu_create(AGENTX_MSG_REGISTER); |
188 | 0 | if (pdu == NULL) { |
189 | 0 | return 0; |
190 | 0 | } |
191 | 0 | pdu->time = timeout; |
192 | 0 | pdu->priority = priority; |
193 | 0 | pdu->sessid = ss->sessid; |
194 | 0 | pdu->range_subid = range_subid; |
195 | 0 | if (contextName) { |
196 | 0 | pdu->flags |= AGENTX_MSG_FLAG_NON_DEFAULT_CONTEXT; |
197 | 0 | pdu->community = (u_char *) strdup(contextName); |
198 | 0 | pdu->community_len = strlen(contextName); |
199 | 0 | } |
200 | |
|
201 | 0 | if (flags & FULLY_QUALIFIED_INSTANCE) { |
202 | 0 | pdu->flags |= AGENTX_MSG_FLAG_INSTANCE_REGISTER; |
203 | 0 | } |
204 | |
|
205 | 0 | if (range_subid) { |
206 | 0 | snmp_pdu_add_variable(pdu, start, startlen, ASN_OBJECT_ID, |
207 | 0 | (u_char *) start, startlen * sizeof(oid)); |
208 | 0 | pdu->variables->val.objid[range_subid - 1] = range_ubound; |
209 | 0 | } else { |
210 | 0 | snmp_add_null_var(pdu, start, startlen); |
211 | 0 | } |
212 | |
|
213 | 0 | if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) { |
214 | 0 | DEBUGMSGTL(("agentx/subagent", "registering failed!\n")); |
215 | 0 | return 0; |
216 | 0 | } |
217 | | |
218 | 0 | if (response->errstat != SNMP_ERR_NOERROR) { |
219 | 0 | snmp_log(LOG_ERR,"registering pdu failed: %ld!\n", response->errstat); |
220 | 0 | snmp_free_pdu(response); |
221 | 0 | return 0; |
222 | 0 | } |
223 | | |
224 | 0 | snmp_free_pdu(response); |
225 | 0 | DEBUGMSGTL(("agentx/subagent", "registered\n")); |
226 | 0 | return 1; |
227 | 0 | } |
228 | | |
229 | | int |
230 | | agentx_unregister(netsnmp_session * ss, oid start[], size_t startlen, |
231 | | int priority, int range_subid, oid range_ubound, |
232 | | const char *contextName) |
233 | 0 | { |
234 | 0 | netsnmp_pdu *pdu, *response; |
235 | |
|
236 | 0 | if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { |
237 | 0 | return 0; |
238 | 0 | } |
239 | | |
240 | 0 | DEBUGMSGTL(("agentx/subagent", "unregistering: ")); |
241 | 0 | DEBUGMSGOIDRANGE(("agentx/subagent", start, startlen, range_subid, |
242 | 0 | range_ubound)); |
243 | 0 | DEBUGMSG(("agentx/subagent", "\n")); |
244 | 0 | pdu = snmp_pdu_create(AGENTX_MSG_UNREGISTER); |
245 | 0 | if (pdu == NULL) { |
246 | 0 | return 0; |
247 | 0 | } |
248 | 0 | pdu->time = 0; |
249 | 0 | pdu->priority = priority; |
250 | 0 | pdu->sessid = ss->sessid; |
251 | 0 | pdu->range_subid = range_subid; |
252 | 0 | if (contextName) { |
253 | 0 | pdu->flags |= AGENTX_MSG_FLAG_NON_DEFAULT_CONTEXT; |
254 | 0 | pdu->community = (u_char *) strdup(contextName); |
255 | 0 | pdu->community_len = strlen(contextName); |
256 | 0 | } |
257 | |
|
258 | 0 | if (range_subid) { |
259 | 0 | snmp_pdu_add_variable(pdu, start, startlen, ASN_OBJECT_ID, |
260 | 0 | (u_char *) start, startlen * sizeof(oid)); |
261 | 0 | pdu->variables->val.objid[range_subid - 1] = range_ubound; |
262 | 0 | } else { |
263 | 0 | snmp_add_null_var(pdu, start, startlen); |
264 | 0 | } |
265 | |
|
266 | 0 | if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) |
267 | 0 | return 0; |
268 | | |
269 | 0 | if (response->errstat != SNMP_ERR_NOERROR) { |
270 | 0 | snmp_free_pdu(response); |
271 | 0 | return 0; |
272 | 0 | } |
273 | | |
274 | 0 | snmp_free_pdu(response); |
275 | 0 | DEBUGMSGTL(("agentx/subagent", "unregistered\n")); |
276 | 0 | return 1; |
277 | 0 | } |
278 | | |
279 | | netsnmp_variable_list * |
280 | | agentx_register_index(netsnmp_session * ss, |
281 | | netsnmp_variable_list * varbind, int flags) |
282 | 0 | { |
283 | 0 | netsnmp_pdu *pdu, *response; |
284 | 0 | netsnmp_variable_list *varbind2; |
285 | |
|
286 | 0 | if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { |
287 | 0 | return NULL; |
288 | 0 | } |
289 | | |
290 | | /* |
291 | | * Make a copy of the index request varbind |
292 | | * for the AgentX request PDU |
293 | | * (since the pdu structure will be freed) |
294 | | */ |
295 | 0 | varbind2 = |
296 | 0 | (netsnmp_variable_list *) malloc(sizeof(netsnmp_variable_list)); |
297 | 0 | if (varbind2 == NULL) |
298 | 0 | return NULL; |
299 | 0 | if (snmp_clone_var(varbind, varbind2)) { |
300 | 0 | snmp_free_varbind(varbind2); |
301 | 0 | return NULL; |
302 | 0 | } |
303 | 0 | if (varbind2->val.string == NULL) |
304 | 0 | varbind2->val.string = varbind2->buf; /* ensure it points somewhere */ |
305 | |
|
306 | 0 | pdu = snmp_pdu_create(AGENTX_MSG_INDEX_ALLOCATE); |
307 | 0 | if (pdu == NULL) { |
308 | 0 | snmp_free_varbind(varbind2); |
309 | 0 | return NULL; |
310 | 0 | } |
311 | 0 | pdu->time = 0; |
312 | 0 | pdu->sessid = ss->sessid; |
313 | 0 | if (flags == ALLOCATE_ANY_INDEX) |
314 | 0 | pdu->flags |= AGENTX_MSG_FLAG_ANY_INSTANCE; |
315 | 0 | if (flags == ALLOCATE_NEW_INDEX) |
316 | 0 | pdu->flags |= AGENTX_MSG_FLAG_NEW_INSTANCE; |
317 | | |
318 | | /* |
319 | | * Just send a single index request varbind. |
320 | | * Although the AgentX protocol supports |
321 | | * multiple index allocations in a single |
322 | | * request, the model used in the net-snmp agent |
323 | | * doesn't currently take advantage of this. |
324 | | * I believe this is our prerogative - just as |
325 | | * long as the master side Index request handler |
326 | | * can cope with multiple index requests. |
327 | | */ |
328 | 0 | pdu->variables = varbind2; |
329 | |
|
330 | 0 | if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) |
331 | 0 | return NULL; |
332 | | |
333 | 0 | if (response->errstat != SNMP_ERR_NOERROR) { |
334 | 0 | snmp_free_pdu(response); |
335 | 0 | return NULL; |
336 | 0 | } |
337 | | |
338 | | /* |
339 | | * Unlink the (single) response varbind to return |
340 | | * to the main driving index request routine. |
341 | | * |
342 | | * This is a memory leak, as nothing will ever |
343 | | * release this varbind. If this becomes a problem, |
344 | | * we'll need to keep a list of these here, and |
345 | | * free the memory in the "index release" routine. |
346 | | * But the master side never frees these either (by |
347 | | * design, since it still needs them), so expecting |
348 | | * the subagent to is discrimination, pure & simple :-) |
349 | | */ |
350 | 0 | varbind2 = response->variables; |
351 | 0 | response->variables = NULL; |
352 | 0 | snmp_free_pdu(response); |
353 | 0 | return varbind2; |
354 | 0 | } |
355 | | |
356 | | int |
357 | | agentx_unregister_index(netsnmp_session * ss, |
358 | | netsnmp_variable_list * varbind) |
359 | 0 | { |
360 | 0 | netsnmp_pdu *pdu, *response; |
361 | 0 | netsnmp_variable_list *varbind2; |
362 | |
|
363 | 0 | if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { |
364 | 0 | return -1; |
365 | 0 | } |
366 | | |
367 | | /* |
368 | | * Make a copy of the index request varbind |
369 | | * for the AgentX request PDU |
370 | | * (since the pdu structure will be freed) |
371 | | */ |
372 | 0 | varbind2 = |
373 | 0 | (netsnmp_variable_list *) malloc(sizeof(netsnmp_variable_list)); |
374 | 0 | if (varbind2 == NULL) |
375 | 0 | return -1; |
376 | 0 | if (snmp_clone_var(varbind, varbind2)) { |
377 | 0 | snmp_free_varbind(varbind2); |
378 | 0 | return -1; |
379 | 0 | } |
380 | | |
381 | 0 | pdu = snmp_pdu_create(AGENTX_MSG_INDEX_DEALLOCATE); |
382 | 0 | if (pdu == NULL) { |
383 | 0 | snmp_free_varbind(varbind2); |
384 | 0 | return -1; |
385 | 0 | } |
386 | 0 | pdu->time = 0; |
387 | 0 | pdu->sessid = ss->sessid; |
388 | | |
389 | | /* |
390 | | * Just send a single index release varbind. |
391 | | * (as above) |
392 | | */ |
393 | 0 | pdu->variables = varbind2; |
394 | |
|
395 | 0 | if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) |
396 | 0 | return -1; |
397 | | |
398 | 0 | if (response->errstat != SNMP_ERR_NOERROR) { |
399 | 0 | snmp_free_pdu(response); |
400 | 0 | return -1; /* XXX - say why */ |
401 | 0 | } |
402 | | |
403 | 0 | snmp_free_pdu(response); |
404 | 0 | return SNMP_ERR_NOERROR; |
405 | 0 | } |
406 | | |
407 | | int |
408 | | agentx_add_agentcaps(netsnmp_session * ss, |
409 | | const oid * agent_cap, size_t agent_caplen, |
410 | | const char *descr) |
411 | 0 | { |
412 | 0 | netsnmp_pdu *pdu, *response; |
413 | |
|
414 | 0 | if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { |
415 | 0 | return 0; |
416 | 0 | } |
417 | | |
418 | 0 | pdu = snmp_pdu_create(AGENTX_MSG_ADD_AGENT_CAPS); |
419 | 0 | if (pdu == NULL) |
420 | 0 | return 0; |
421 | 0 | pdu->time = 0; |
422 | 0 | pdu->sessid = ss->sessid; |
423 | 0 | snmp_add_var(pdu, agent_cap, agent_caplen, 's', descr); |
424 | |
|
425 | 0 | if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) |
426 | 0 | return 0; |
427 | | |
428 | 0 | if (response->errstat != SNMP_ERR_NOERROR) { |
429 | 0 | snmp_free_pdu(response); |
430 | 0 | return 0; |
431 | 0 | } |
432 | | |
433 | 0 | snmp_free_pdu(response); |
434 | 0 | return 1; |
435 | 0 | } |
436 | | |
437 | | int |
438 | | agentx_remove_agentcaps(netsnmp_session * ss, |
439 | | const oid * agent_cap, size_t agent_caplen) |
440 | 0 | { |
441 | 0 | netsnmp_pdu *pdu, *response; |
442 | |
|
443 | 0 | if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { |
444 | 0 | return 0; |
445 | 0 | } |
446 | | |
447 | 0 | pdu = snmp_pdu_create(AGENTX_MSG_REMOVE_AGENT_CAPS); |
448 | 0 | if (pdu == NULL) |
449 | 0 | return 0; |
450 | 0 | pdu->time = 0; |
451 | 0 | pdu->sessid = ss->sessid; |
452 | 0 | snmp_add_null_var(pdu, agent_cap, agent_caplen); |
453 | |
|
454 | 0 | if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) |
455 | 0 | return 0; |
456 | | |
457 | 0 | if (response->errstat != SNMP_ERR_NOERROR) { |
458 | 0 | snmp_free_pdu(response); |
459 | 0 | return 0; |
460 | 0 | } |
461 | | |
462 | 0 | snmp_free_pdu(response); |
463 | 0 | return 1; |
464 | 0 | } |
465 | | |
466 | | int |
467 | | agentx_send_ping(netsnmp_session * ss) |
468 | 0 | { |
469 | 0 | netsnmp_pdu *pdu, *response; |
470 | |
|
471 | 0 | if (ss == NULL || !IS_AGENTX_VERSION(ss->version)) { |
472 | 0 | return 0; |
473 | 0 | } |
474 | | |
475 | 0 | pdu = snmp_pdu_create(AGENTX_MSG_PING); |
476 | 0 | if (pdu == NULL) |
477 | 0 | return 0; |
478 | 0 | pdu->time = 0; |
479 | 0 | pdu->sessid = ss->sessid; |
480 | |
|
481 | 0 | if (agentx_synch_response(ss, pdu, &response) != STAT_SUCCESS) |
482 | 0 | return 0; |
483 | | |
484 | 0 | if (response->errstat != SNMP_ERR_NOERROR) { |
485 | 0 | snmp_free_pdu(response); |
486 | 0 | return 0; |
487 | 0 | } |
488 | | |
489 | 0 | snmp_free_pdu(response); |
490 | 0 | return 1; |
491 | 0 | } |