1 /*
2 * baby_steps.c
3 * $Id$
4 *
5 * Portions of this file are copyrighted by:
6 * Copyright (c) 2016 VMware, Inc. All rights reserved.
7 * Use is subject to license terms specified in the COPYING file
8 * distributed with the Net-SNMP package.
9 */
10 #include <net-snmp/net-snmp-config.h>
11 #include <net-snmp/net-snmp-features.h>
12 #include <net-snmp/net-snmp-includes.h>
13 #include <net-snmp/agent/net-snmp-agent-includes.h>
14
15 netsnmp_feature_provide(baby_steps);
16 netsnmp_feature_child_of(baby_steps, mib_helpers);
17
18 #ifdef NETSNMP_FEATURE_REQUIRE_BABY_STEPS
19 netsnmp_feature_require(check_requests_error);
20 #endif
21
22 #ifndef NETSNMP_FEATURE_REMOVE_BABY_STEPS
23
24 #include <net-snmp/agent/baby_steps.h>
25
26 #define BABY_STEPS_PER_MODE_MAX 4
27 #define BSTEP_USE_ORIGINAL 0xffff
28
29 static u_short get_mode_map[BABY_STEPS_PER_MODE_MAX] = {
30 MODE_BSTEP_PRE_REQUEST, MODE_BSTEP_OBJECT_LOOKUP, BSTEP_USE_ORIGINAL, MODE_BSTEP_POST_REQUEST };
31
32 #ifndef NETSNMP_NO_WRITE_SUPPORT
33 static u_short set_mode_map[SNMP_MSG_INTERNAL_SET_MAX][BABY_STEPS_PER_MODE_MAX] = {
34 /*R1*/
35 { MODE_BSTEP_PRE_REQUEST, MODE_BSTEP_OBJECT_LOOKUP, MODE_BSTEP_ROW_CREATE,
36 MODE_BSTEP_CHECK_VALUE },
37 /*R2*/
38 { MODE_BSTEP_UNDO_SETUP, BABY_STEP_NONE, BABY_STEP_NONE, BABY_STEP_NONE },
39 /*A */
40 { MODE_BSTEP_SET_VALUE,MODE_BSTEP_CHECK_CONSISTENCY,
41 MODE_BSTEP_COMMIT, BABY_STEP_NONE },
42 /*C */
43 { MODE_BSTEP_IRREVERSIBLE_COMMIT, MODE_BSTEP_UNDO_CLEANUP, MODE_BSTEP_POST_REQUEST,
44 BABY_STEP_NONE},
45 /*F */
46 { MODE_BSTEP_UNDO_CLEANUP, MODE_BSTEP_POST_REQUEST, BABY_STEP_NONE,
47 BABY_STEP_NONE },
48 /*U */
49 { MODE_BSTEP_UNDO_COMMIT, MODE_BSTEP_UNDO_SET, MODE_BSTEP_UNDO_CLEANUP,
50 MODE_BSTEP_POST_REQUEST}
51 };
52 #endif /* NETSNMP_NO_WRITE_SUPPORT */
53
54 static int
55 _baby_steps_helper(netsnmp_mib_handler *handler,
56 netsnmp_handler_registration *reginfo,
57 netsnmp_agent_request_info *reqinfo,
58 netsnmp_request_info *requests);
59 static int
60 _baby_steps_access_multiplexer(netsnmp_mib_handler *handler,
61 netsnmp_handler_registration *reginfo,
62 netsnmp_agent_request_info *reqinfo,
63 netsnmp_request_info *requests);
64
65 /** @defgroup baby_steps baby_steps
66 * Calls your handler in baby_steps for set processing.
67 * @ingroup handler
68 * @{
69 */
70
71 static netsnmp_baby_steps_modes *
netsnmp_baby_steps_modes_ref(netsnmp_baby_steps_modes * md)72 netsnmp_baby_steps_modes_ref(netsnmp_baby_steps_modes *md)
73 {
74 md->refcnt++;
75 return md;
76 }
77
78 static void
netsnmp_baby_steps_modes_deref(netsnmp_baby_steps_modes * md)79 netsnmp_baby_steps_modes_deref(netsnmp_baby_steps_modes *md)
80 {
81 if (--md->refcnt == 0)
82 free(md);
83 }
84
85 /** returns a baby_steps handler that can be injected into a given
86 * handler chain.
87 */
88 netsnmp_mib_handler *
netsnmp_baby_steps_handler_get(u_long modes)89 netsnmp_baby_steps_handler_get(u_long modes)
90 {
91 netsnmp_mib_handler *mh;
92 netsnmp_baby_steps_modes *md;
93
94 mh = netsnmp_create_handler("baby_steps", _baby_steps_helper);
95 if(!mh)
96 return NULL;
97
98 md = SNMP_MALLOC_TYPEDEF(netsnmp_baby_steps_modes);
99 if (NULL == md) {
100 snmp_log(LOG_ERR,"malloc failed in netsnmp_baby_steps_handler_get\n");
101 netsnmp_handler_free(mh);
102 mh = NULL;
103 }
104 else {
105 md->refcnt = 1;
106 mh->myvoid = md;
107 mh->data_clone = (void *(*)(void *))netsnmp_baby_steps_modes_ref;
108 mh->data_free = (void (*)(void *))netsnmp_baby_steps_modes_deref;
109 if (0 == modes)
110 modes = BABY_STEP_ALL;
111 md->registered = modes;
112 }
113
114 /*
115 * don't set MIB_HANDLER_AUTO_NEXT, since we need to call lower
116 * handlers with a munged mode.
117 */
118
119 return mh;
120 }
121
122 /** @internal Implements the baby_steps handler */
123 static int
_baby_steps_helper(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)124 _baby_steps_helper(netsnmp_mib_handler *handler,
125 netsnmp_handler_registration *reginfo,
126 netsnmp_agent_request_info *reqinfo,
127 netsnmp_request_info *requests)
128 {
129 netsnmp_baby_steps_modes *bs_modes;
130 int save_mode, i, rc = SNMP_ERR_NOERROR;
131 u_short *mode_map_ptr;
132
133 DEBUGMSGTL(("baby_steps", "Got request, mode %s\n",
134 se_find_label_in_slist("agent_mode",reqinfo->mode)));
135
136 bs_modes = (netsnmp_baby_steps_modes*)handler->myvoid;
137 netsnmp_assert(NULL != bs_modes);
138
139 switch (reqinfo->mode) {
140
141 #ifndef NETSNMP_NO_WRITE_SUPPORT
142 case MODE_SET_RESERVE1:
143 /*
144 * clear completed modes
145 * xxx-rks: this will break for pdus with set requests to different
146 * rows in the same table when the handler is set up to use the row
147 * merge helper as well (or if requests are serialized).
148 */
149 bs_modes->completed = 0;
150 /* FALL THROUGH */
151 case MODE_SET_RESERVE2:
152 case MODE_SET_ACTION:
153 case MODE_SET_COMMIT:
154 case MODE_SET_FREE:
155 case MODE_SET_UNDO:
156 mode_map_ptr = set_mode_map[reqinfo->mode];
157 break;
158 #endif /* NETSNMP_NO_WRITE_SUPPORT */
159
160 default:
161 /*
162 * clear completed modes
163 */
164 bs_modes->completed = 0;
165
166 mode_map_ptr = get_mode_map;
167 }
168
169 /*
170 * NOTE: if you update this chart, please update the versions in
171 * local/mib2c-conf.d/parent-set.m2i
172 * agent/mibgroup/helpers/baby_steps.c
173 * while you're at it.
174 */
175 /*
176 ***********************************************************************
177 * Baby Steps Flow Chart (2004.06.05) *
178 * *
179 * +--------------+ +================+ U = unconditional path *
180 * |optional state| ||required state|| S = path for success *
181 * +--------------+ +================+ E = path for error *
182 ***********************************************************************
183 *
184 * +--------------+
185 * | pre |
186 * | request |
187 * +--------------+
188 * | U
189 * +-------------+ +==============+
190 * | row |f|<-------|| object ||
191 * | create |1| E || lookup ||
192 * +-------------+ +==============+
193 * E | | S | S
194 * | +------------------>|
195 * | +==============+
196 * | E || check ||
197 * |<---------------|| values ||
198 * | +==============+
199 * | | S
200 * | +==============+
201 * | +<-------|| undo ||
202 * | | E || setup ||
203 * | | +==============+
204 * | | | S
205 * | | +==============+
206 * | | || set ||-------------------------->+
207 * | | || value || E |
208 * | | +==============+ |
209 * | | | S |
210 * | | +--------------+ |
211 * | | | check |-------------------------->|
212 * | | | consistency | E |
213 * | | +--------------+ |
214 * | | | S |
215 * | | +==============+ +==============+ |
216 * | | || commit ||-------->|| undo || |
217 * | | || || E || commit || |
218 * | | +==============+ +==============+ |
219 * | | | S U |<--------+
220 * | | +--------------+ +==============+
221 * | | | irreversible | || undo ||
222 * | | | commit | || set ||
223 * | | +--------------+ +==============+
224 * | | | U U |
225 * | +-------------->|<------------------------+
226 * | +==============+
227 * | || undo ||
228 * | || cleanup ||
229 * | +==============+
230 * +---------------------->| U
231 * |
232 * (err && f1)------------------->+
233 * | |
234 * +--------------+ +--------------+
235 * | post |<--------| row |
236 * | request | U | release |
237 * +--------------+ +--------------+
238 *
239 */
240 /*
241 * save original mode
242 */
243 save_mode = reqinfo->mode;
244 for(i = 0; i < BABY_STEPS_PER_MODE_MAX; ++i ) {
245 /*
246 * break if we run out of baby steps for this mode
247 */
248 if(mode_map_ptr[i] == BABY_STEP_NONE)
249 break;
250
251 DEBUGMSGTL(("baby_steps", " baby step mode %s\n",
252 se_find_label_in_slist("babystep_mode",mode_map_ptr[i])));
253
254 /*
255 * skip modes the handler didn't register for
256 */
257 if (BSTEP_USE_ORIGINAL != mode_map_ptr[i]) {
258 u_int mode_flag;
259
260 #ifndef NETSNMP_NO_WRITE_SUPPORT
261 /*
262 * skip undo commit if commit wasn't hit, and
263 * undo_cleanup if undo_setup wasn't hit.
264 */
265 if((MODE_SET_UNDO == save_mode) &&
266 (MODE_BSTEP_UNDO_COMMIT == mode_map_ptr[i]) &&
267 !(BABY_STEP_COMMIT & bs_modes->completed)) {
268 DEBUGMSGTL(("baby_steps",
269 " skipping commit undo (no commit)\n"));
270 continue;
271 }
272 else if((MODE_SET_FREE == save_mode) &&
273 (MODE_BSTEP_UNDO_CLEANUP == mode_map_ptr[i]) &&
274 !(BABY_STEP_UNDO_SETUP & bs_modes->completed)) {
275 DEBUGMSGTL(("baby_steps",
276 " skipping undo cleanup (no undo setup)\n"));
277 continue;
278 }
279 #endif /* NETSNMP_NO_WRITE_SUPPORT */
280
281 reqinfo->mode = mode_map_ptr[i];
282 mode_flag = netsnmp_baby_step_mode2flag( mode_map_ptr[i] );
283 if((mode_flag & bs_modes->registered))
284 bs_modes->completed |= mode_flag;
285 else {
286 DEBUGMSGTL(("baby_steps",
287 " skipping mode (not registered)\n"));
288 continue;
289 }
290
291
292 }
293 else {
294 reqinfo->mode = save_mode;
295 }
296
297 #ifdef BABY_STEPS_NEXT_MODE
298 /*
299 * I can't remember why I wanted the next mode in the request,
300 * but it's not used anywhere, so don't use this code. saved,
301 * in case I remember why I thought needed it. - rstory 040911
302 */
303 if((BABY_STEPS_PER_MODE_MAX - 1) == i)
304 reqinfo->next_mode_ok = BABY_STEP_NONE;
305 else {
306 if(BSTEP_USE_ORIGINAL == mode_map_ptr[i+1])
307 reqinfo->next_mode_ok = save_mode;
308 else
309 reqinfo->next_mode_ok = mode_map_ptr[i+1];
310 }
311 #endif
312
313 /*
314 * call handlers for baby step
315 */
316 rc = netsnmp_call_next_handler(handler, reginfo, reqinfo,
317 requests);
318
319 /*
320 * check for error calling handler (unlikely, but...)
321 */
322 if(rc) {
323 DEBUGMSGTL(("baby_steps", " ERROR:handler error\n"));
324 break;
325 }
326
327 /*
328 * check for errors in any of the requests for GET-like, reserve1,
329 * reserve2 and action. (there is no recovery from errors
330 * in commit, free or undo.)
331 */
332 if (MODE_IS_GET(save_mode)
333 #ifndef NETSNMP_NO_WRITE_SUPPORT
334 || (save_mode < SNMP_MSG_INTERNAL_SET_COMMIT)
335 #endif /* NETSNMP_NO_WRITE_SUPPORT */
336 ) {
337 rc = netsnmp_check_requests_error(requests);
338 if(rc) {
339 DEBUGMSGTL(("baby_steps", " ERROR:request error\n"));
340 break;
341 }
342 }
343 }
344
345 /*
346 * restore original mode
347 */
348 reqinfo->mode = save_mode;
349
350
351 return rc;
352 }
353
354 /** initializes the baby_steps helper which then registers a baby_steps
355 * handler as a run-time injectable handler for configuration file
356 * use.
357 */
358 netsnmp_feature_child_of(netsnmp_baby_steps_handler_init,netsnmp_unused);
359 #ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_BABY_STEPS_HANDLER_INIT
360 void
netsnmp_baby_steps_handler_init(void)361 netsnmp_baby_steps_handler_init(void)
362 {
363 netsnmp_mib_handler *handler =
364 netsnmp_baby_steps_handler_get(BABY_STEP_ALL);
365 if (NULL == handler) {
366 netsnmp_handler_free(handler);
367 snmp_log(LOG_ERR, "could not create baby steps handler\n");
368 return;
369 }
370
371 netsnmp_register_handler_by_name("baby_steps", handler);
372 }
373 #endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_BABY_STEPS_HANDLER_INIT */
374
375 /** @} */
376
377 /** @defgroup access_multiplexer baby_steps_access_multiplexer: calls individual access methods based on baby_step mode.
378 * @ingroup baby_steps
379 * @{
380 */
381
382 /** returns a baby_steps handler that can be injected into a given
383 * handler chain.
384 */
385 netsnmp_mib_handler *
netsnmp_baby_steps_access_multiplexer_get(netsnmp_baby_steps_access_methods * am)386 netsnmp_baby_steps_access_multiplexer_get(netsnmp_baby_steps_access_methods *am)
387 {
388 netsnmp_mib_handler *mh;
389
390 mh = netsnmp_create_handler("baby_steps_mux",
391 _baby_steps_access_multiplexer);
392 if(!mh)
393 return NULL;
394
395 mh->myvoid = am;
396 mh->flags |= MIB_HANDLER_AUTO_NEXT;
397
398 return mh;
399 }
400
401 /** @internal Implements the baby_steps handler */
402 static int
_baby_steps_access_multiplexer(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)403 _baby_steps_access_multiplexer(netsnmp_mib_handler *handler,
404 netsnmp_handler_registration *reginfo,
405 netsnmp_agent_request_info *reqinfo,
406 netsnmp_request_info *requests)
407 {
408 void *temp_void;
409 Netsnmp_Node_Handler *method = NULL;
410 netsnmp_baby_steps_access_methods *access_methods;
411 int rc = SNMP_ERR_NOERROR;
412
413 /** call handlers should enforce these */
414 netsnmp_assert((handler!=NULL) && (reginfo!=NULL) && (reqinfo!=NULL) &&
415 (requests!=NULL));
416
417 DEBUGMSGT(("baby_steps_mux", "mode %s\n",
418 se_find_label_in_slist("babystep_mode",reqinfo->mode)));
419
420 access_methods = (netsnmp_baby_steps_access_methods *)handler->myvoid;
421 if(!access_methods) {
422 snmp_log(LOG_ERR,"baby_steps_access_multiplexer has no methods\n");
423 return SNMPERR_GENERR;
424 }
425
426 switch(reqinfo->mode) {
427
428 case MODE_BSTEP_PRE_REQUEST:
429 if( access_methods->pre_request )
430 method = access_methods->pre_request;
431 break;
432
433 case MODE_BSTEP_OBJECT_LOOKUP:
434 if( access_methods->object_lookup )
435 method = access_methods->object_lookup;
436 break;
437
438 case SNMP_MSG_GET:
439 case SNMP_MSG_GETNEXT:
440 if( access_methods->get_values )
441 method = access_methods->get_values;
442 break;
443
444 #ifndef NETSNMP_NO_WRITE_SUPPORT
445 case MODE_BSTEP_CHECK_VALUE:
446 if( access_methods->object_syntax_checks )
447 method = access_methods->object_syntax_checks;
448 break;
449
450 case MODE_BSTEP_ROW_CREATE:
451 if( access_methods->row_creation )
452 method = access_methods->row_creation;
453 break;
454
455 case MODE_BSTEP_UNDO_SETUP:
456 if( access_methods->undo_setup )
457 method = access_methods->undo_setup;
458 break;
459
460 case MODE_BSTEP_SET_VALUE:
461 if( access_methods->set_values )
462 method = access_methods->set_values;
463 break;
464
465 case MODE_BSTEP_CHECK_CONSISTENCY:
466 if( access_methods->consistency_checks )
467 method = access_methods->consistency_checks;
468 break;
469
470 case MODE_BSTEP_UNDO_SET:
471 if( access_methods->undo_sets )
472 method = access_methods->undo_sets;
473 break;
474
475 case MODE_BSTEP_COMMIT:
476 if( access_methods->commit )
477 method = access_methods->commit;
478 break;
479
480 case MODE_BSTEP_UNDO_COMMIT:
481 if( access_methods->undo_commit )
482 method = access_methods->undo_commit;
483 break;
484
485 case MODE_BSTEP_IRREVERSIBLE_COMMIT:
486 if( access_methods->irreversible_commit )
487 method = access_methods->irreversible_commit;
488 break;
489
490 case MODE_BSTEP_UNDO_CLEANUP:
491 if( access_methods->undo_cleanup )
492 method = access_methods->undo_cleanup;
493 break;
494 #endif /* NETSNMP_NO_WRITE_SUPPORT */
495
496 case MODE_BSTEP_POST_REQUEST:
497 if( access_methods->post_request )
498 method = access_methods->post_request;
499 break;
500
501 default:
502 snmp_log(LOG_ERR,"unknown mode %d\n", reqinfo->mode);
503 return SNMP_ERR_GENERR;
504 }
505
506 /*
507 * if method exists, set up handler void and call method.
508 */
509 if(NULL != method) {
510 temp_void = handler->myvoid;
511 handler->myvoid = access_methods->my_access_void;
512 rc = (*method)(handler, reginfo, reqinfo, requests);
513 handler->myvoid = temp_void;
514 }
515 else {
516 rc = SNMP_ERR_GENERR;
517 snmp_log(LOG_ERR,"baby steps multiplexer handler called for a mode "
518 "with no handler\n");
519 netsnmp_assert(NULL != method);
520 }
521
522 /*
523 * don't call any lower handlers, it will be done for us
524 * since we set MIB_HANDLER_AUTO_NEXT
525 */
526
527 return rc;
528 }
529
530 /*
531 * give a baby step mode, return the flag for that mode
532 */
533 int
netsnmp_baby_step_mode2flag(u_int mode)534 netsnmp_baby_step_mode2flag( u_int mode )
535 {
536 switch( mode ) {
537 case MODE_BSTEP_OBJECT_LOOKUP:
538 return BABY_STEP_OBJECT_LOOKUP;
539 #ifndef NETSNMP_NO_WRITE_SUPPORT
540 case MODE_BSTEP_SET_VALUE:
541 return BABY_STEP_SET_VALUE;
542 case MODE_BSTEP_IRREVERSIBLE_COMMIT:
543 return BABY_STEP_IRREVERSIBLE_COMMIT;
544 case MODE_BSTEP_CHECK_VALUE:
545 return BABY_STEP_CHECK_VALUE;
546 case MODE_BSTEP_PRE_REQUEST:
547 return BABY_STEP_PRE_REQUEST;
548 case MODE_BSTEP_POST_REQUEST:
549 return BABY_STEP_POST_REQUEST;
550 case MODE_BSTEP_UNDO_SETUP:
551 return BABY_STEP_UNDO_SETUP;
552 case MODE_BSTEP_UNDO_CLEANUP:
553 return BABY_STEP_UNDO_CLEANUP;
554 case MODE_BSTEP_UNDO_SET:
555 return BABY_STEP_UNDO_SET;
556 case MODE_BSTEP_ROW_CREATE:
557 return BABY_STEP_ROW_CREATE;
558 case MODE_BSTEP_CHECK_CONSISTENCY:
559 return BABY_STEP_CHECK_CONSISTENCY;
560 case MODE_BSTEP_COMMIT:
561 return BABY_STEP_COMMIT;
562 case MODE_BSTEP_UNDO_COMMIT:
563 return BABY_STEP_UNDO_COMMIT;
564 #endif /* NETSNMP_NO_WRITE_SUPPORT */
565 default:
566 netsnmp_assert("unknown flag");
567 break;
568 }
569 return 0;
570 }
571 /** @} */
572
573 #else /* NETSNMP_FEATURE_REMOVE_BABY_STEPS */
574 netsnmp_feature_unused(baby_steps);
575 #endif /* NETSNMP_FEATURE_REMOVE_BABY_STEPS */
576
577