xref: /freebsd/contrib/bsnmp/snmp_target/target_snmp.c (revision 193d9e768ba63fcfb187cfd17f461f7d41345048)
1 /*-
2  * Copyright (c) 2010 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Shteryana Sotirova Shopova under
6  * sponsorship from the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31 #include <sys/queue.h>
32 #include <sys/types.h>
33 
34 #include <errno.h>
35 #include <stdarg.h>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <stdint.h>
39 #include <string.h>
40 #include <syslog.h>
41 
42 #include "asn1.h"
43 #include "snmp.h"
44 #include "snmpmod.h"
45 
46 #include "target_tree.h"
47 #include "target_oid.h"
48 
49 static struct lmodule *target_module;
50 /* For the registration. */
51 static const struct asn_oid oid_target = OIDX_snmpTargetMIB;
52 static const struct asn_oid oid_notification = OIDX_snmpNotificationMIB;
53 
54 static uint reg_target;
55 static uint reg_notification;
56 
57 static int32_t target_lock;
58 
59 static const struct asn_oid oid_udp_domain = OIDX_snmpUDPDomain;
60 
61 /*
62  * Internal datastructures and forward declarations.
63  */
64 static void		target_append_index(struct asn_oid *, uint,
65     const char *);
66 static int		target_decode_index(const struct asn_oid *, uint,
67     char *);
68 static struct target_address *target_get_address(const struct asn_oid *,
69     uint);
70 static struct target_address *target_get_next_address(const struct asn_oid *,
71     uint);
72 static struct target_param *target_get_param(const struct asn_oid *,
73     uint);
74 static struct target_param *target_get_next_param(const struct asn_oid *,
75     uint);
76 static struct target_notify *target_get_notify(const struct asn_oid *,
77     uint);
78 static struct target_notify *target_get_next_notify(const struct asn_oid *,
79     uint);
80 
81 int
82 op_snmp_target(struct snmp_context *ctx __unused, struct snmp_value *val,
83     uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
84 {
85 	struct snmpd_target_stats *ctx_stats;
86 
87 	if (val->var.subs[sub - 1] == LEAF_snmpTargetSpinLock) {
88 		switch (op) {
89 		case SNMP_OP_GET:
90 			if (++target_lock == INT32_MAX)
91 				target_lock = 0;
92 			val->v.integer = target_lock;
93 			break;
94 		case SNMP_OP_GETNEXT:
95 			abort();
96 		case SNMP_OP_SET:
97 			if (val->v.integer != target_lock)
98 				return (SNMP_ERR_INCONS_VALUE);
99 			break;
100 		case SNMP_OP_ROLLBACK:
101 			/* FALLTHROUGH */
102 		case SNMP_OP_COMMIT:
103 			break;
104 		}
105 		return (SNMP_ERR_NOERROR);
106 	} else if (op == SNMP_OP_SET)
107 		return (SNMP_ERR_NOT_WRITEABLE);
108 
109 	if ((ctx_stats = bsnmpd_get_target_stats()) == NULL)
110 		return (SNMP_ERR_GENERR);
111 
112 	if (op == SNMP_OP_GET) {
113 		switch (val->var.subs[sub - 1]) {
114 		case LEAF_snmpUnavailableContexts:
115 			val->v.uint32 = ctx_stats->unavail_contexts;
116 			break;
117 		case LEAF_snmpUnknownContexts:
118 			val->v.uint32 = ctx_stats->unknown_contexts;
119 			break;
120 		default:
121 			return (SNMP_ERR_NOSUCHNAME);
122 		}
123 		return (SNMP_ERR_NOERROR);
124 	}
125 	abort();
126 }
127 
128 int
129 op_snmp_target_addrs(struct snmp_context *ctx __unused, struct snmp_value *val,
130     uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
131 {
132 	char aname[SNMP_ADM_STR32_SIZ];
133 	struct target_address *addrs;
134 
135 	switch (op) {
136 	case SNMP_OP_GET:
137 		if ((addrs = target_get_address(&val->var, sub)) == NULL)
138 			return (SNMP_ERR_NOSUCHNAME);
139 		break;
140 
141 	case SNMP_OP_GETNEXT:
142 		if ((addrs = target_get_next_address(&val->var, sub)) == NULL)
143 			return (SNMP_ERR_NOSUCHNAME);
144 		target_append_index(&val->var, sub, addrs->name);
145 		break;
146 
147 	case SNMP_OP_SET:
148 		if ((addrs = target_get_address(&val->var, sub)) == NULL &&
149 		    (val->var.subs[sub - 1] != LEAF_snmpTargetAddrRowStatus ||
150 		    val->v.integer != RowStatus_createAndWait))
151 			return (SNMP_ERR_NOSUCHNAME);
152 
153 		if (addrs != NULL) {
154 			if (community != COMM_INITIALIZE &&
155 			    addrs->type == StorageType_readOnly)
156 				return (SNMP_ERR_NOT_WRITEABLE);
157 			if (addrs->status == RowStatus_active &&
158 			    val->v.integer != RowStatus_destroy)
159 				return (SNMP_ERR_INCONS_VALUE);
160 		}
161 
162 		switch (val->var.subs[sub - 1]) {
163 		case LEAF_snmpTargetAddrTDomain:
164 			return (SNMP_ERR_INCONS_VALUE);
165 		case LEAF_snmpTargetAddrTAddress:
166 			if (val->v.octetstring.len != SNMP_UDP_ADDR_SIZ)
167 				return (SNMP_ERR_INCONS_VALUE);
168 			ctx->scratch->ptr1 = malloc(SNMP_UDP_ADDR_SIZ);
169 			if (ctx->scratch->ptr1 == NULL)
170 				return (SNMP_ERR_GENERR);
171 			memcpy(ctx->scratch->ptr1, addrs->address,
172 			    SNMP_UDP_ADDR_SIZ);
173 			memcpy(addrs->address, val->v.octetstring.octets,
174 			    SNMP_UDP_ADDR_SIZ);
175 			break;
176 
177 		case LEAF_snmpTargetAddrTagList:
178 			if (val->v.octetstring.len >= SNMP_TAG_SIZ)
179 				return (SNMP_ERR_INCONS_VALUE);
180 			ctx->scratch->int1 = strlen(addrs->taglist) + 1;
181 			ctx->scratch->ptr1 = malloc(ctx->scratch->int1);
182 			if (ctx->scratch->ptr1 == NULL)
183 				return (SNMP_ERR_GENERR);
184 			strlcpy(ctx->scratch->ptr1, addrs->taglist,
185 			    ctx->scratch->int1);
186 			memcpy(addrs->taglist, val->v.octetstring.octets,
187 			    val->v.octetstring.len);
188 			addrs->taglist[val->v.octetstring.len] = '\0';
189 			break;
190 
191 		case LEAF_snmpTargetAddrParams:
192 			if (val->v.octetstring.len >= SNMP_ADM_STR32_SIZ)
193 				return (SNMP_ERR_INCONS_VALUE);
194 			ctx->scratch->int1 = strlen(addrs->paramname) + 1;
195 			ctx->scratch->ptr1 = malloc(ctx->scratch->int1);
196 			if (ctx->scratch->ptr1 == NULL)
197 				return (SNMP_ERR_GENERR);
198 			strlcpy(ctx->scratch->ptr1, addrs->paramname,
199 			    ctx->scratch->int1);
200 			memcpy(addrs->paramname, val->v.octetstring.octets,
201 			    val->v.octetstring.len);
202 			addrs->paramname[val->v.octetstring.len] = '\0';
203 			break;
204 
205 		case LEAF_snmpTargetAddrRetryCount:
206 			ctx->scratch->int1 = addrs->retry;
207 			addrs->retry = val->v.integer;
208 			break;
209 
210 		case LEAF_snmpTargetAddrTimeout:
211 			ctx->scratch->int1 = addrs->timeout;
212 			addrs->timeout = val->v.integer / 10;
213 			break;
214 
215 		case LEAF_snmpTargetAddrStorageType:
216 			return (SNMP_ERR_INCONS_VALUE);
217 
218 		case LEAF_snmpTargetAddrRowStatus:
219 			if (addrs != NULL) {
220 				if (val->v.integer != RowStatus_active &&
221 				    val->v.integer != RowStatus_destroy)
222 					return (SNMP_ERR_INCONS_VALUE);
223 				if (val->v.integer == RowStatus_active &&
224 				    (addrs->address[0] == 0 ||
225 				    strlen(addrs->taglist) == 0 ||
226 				    strlen(addrs->paramname) == 0))
227 					return (SNMP_ERR_INCONS_VALUE);
228 				ctx->scratch->int1 = addrs->status;
229 				addrs->status = val->v.integer;
230 				return (SNMP_ERR_NOERROR);
231 			}
232 			if (val->v.integer != RowStatus_createAndWait ||
233 			    target_decode_index(&val->var, sub, aname) < 0)
234 				return (SNMP_ERR_INCONS_VALUE);
235 			if ((addrs = target_new_address(aname)) == NULL)
236 				return (SNMP_ERR_GENERR);
237 			addrs->status = RowStatus_destroy;
238 			if (community != COMM_INITIALIZE)
239 				addrs->type = StorageType_volatile;
240 			else
241 				addrs->type = StorageType_readOnly;
242 			break;
243 		}
244 		return (SNMP_ERR_NOERROR);
245 
246 	case SNMP_OP_COMMIT:
247 		switch (val->var.subs[sub - 1]) {
248 		case LEAF_snmpTargetAddrTAddress:
249 		case LEAF_snmpTargetAddrTagList:
250 		case LEAF_snmpTargetAddrParams:
251 			free(ctx->scratch->ptr1);
252 			break;
253 		case LEAF_snmpTargetAddrRowStatus:
254 			if ((addrs = target_get_address(&val->var, sub)) == NULL)
255 				return (SNMP_ERR_GENERR);
256 			if (val->v.integer == RowStatus_destroy)
257 				return (target_delete_address(addrs));
258 			else if (val->v.integer == RowStatus_active)
259 				return (target_activate_address(addrs));
260 			break;
261 		default:
262 			break;
263 		}
264 		return (SNMP_ERR_NOERROR);
265 
266 	case SNMP_OP_ROLLBACK:
267 		if ((addrs = target_get_address(&val->var, sub)) == NULL)
268 			return (SNMP_ERR_GENERR);
269 
270 		switch (val->var.subs[sub - 1]) {
271 		case LEAF_snmpTargetAddrTAddress:
272 			memcpy(addrs->address, ctx->scratch->ptr1,
273 			    SNMP_UDP_ADDR_SIZ);
274 			free(ctx->scratch->ptr1);
275 			break;
276 
277 		case LEAF_snmpTargetAddrTagList:
278 			strlcpy(addrs->taglist, ctx->scratch->ptr1,
279 			    ctx->scratch->int1);
280 			free(ctx->scratch->ptr1);
281 			break;
282 
283 		case LEAF_snmpTargetAddrParams:
284 			strlcpy(addrs->paramname, ctx->scratch->ptr1,
285 			    ctx->scratch->int1);
286 			free(ctx->scratch->ptr1);
287 			break;
288 
289 		case LEAF_snmpTargetAddrRetryCount:
290 			addrs->retry = ctx->scratch->int1;
291 			break;
292 
293 		case LEAF_snmpTargetAddrTimeout:
294 			addrs->timeout = ctx->scratch->int1;
295 			break;
296 
297 		case LEAF_snmpTargetAddrRowStatus:
298 			if (ctx->scratch->int1 == RowStatus_destroy)
299 				return (target_delete_address(addrs));
300 			break;
301 		default:
302 			break;
303 		}
304 		return (SNMP_ERR_NOERROR);
305 
306 	default:
307 		abort();
308 	}
309 
310 	switch (val->var.subs[sub - 1]) {
311 	case LEAF_snmpTargetAddrTDomain:
312 		return (oid_get(val, &oid_udp_domain));
313 	case LEAF_snmpTargetAddrTAddress:
314 		return (string_get(val, addrs->address, SNMP_UDP_ADDR_SIZ));
315 	case LEAF_snmpTargetAddrTimeout:
316 		val->v.integer = addrs->timeout;
317 		break;
318 	case LEAF_snmpTargetAddrRetryCount:
319 		val->v.integer = addrs->retry;
320 		break;
321 	case LEAF_snmpTargetAddrTagList:
322 		return (string_get(val, addrs->taglist, -1));
323 	case LEAF_snmpTargetAddrParams:
324 		return (string_get(val, addrs->paramname, -1));
325 	case LEAF_snmpTargetAddrStorageType:
326 		val->v.integer = addrs->type;
327 		break;
328 	case LEAF_snmpTargetAddrRowStatus:
329 		val->v.integer = addrs->status;
330 		break;
331 	default:
332 		abort();
333 	}
334 
335 	return (SNMP_ERR_NOERROR);
336 }
337 
338 int
339 op_snmp_target_params(struct snmp_context *ctx __unused, struct snmp_value *val,
340     uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
341 {
342 	char pname[SNMP_ADM_STR32_SIZ];
343 	struct target_param *param;
344 
345 	switch (op) {
346 	case SNMP_OP_GET:
347 		if ((param = target_get_param(&val->var, sub)) == NULL)
348 			return (SNMP_ERR_NOSUCHNAME);
349 		break;
350 
351 	case SNMP_OP_GETNEXT:
352 		if ((param = target_get_next_param(&val->var, sub)) == NULL)
353 			return (SNMP_ERR_NOSUCHNAME);
354 		target_append_index(&val->var, sub, param->name);
355 		break;
356 
357 	case SNMP_OP_SET:
358 		if ((param = target_get_param(&val->var, sub)) == NULL &&
359 		    (val->var.subs[sub - 1] != LEAF_snmpTargetParamsRowStatus ||
360 		    val->v.integer != RowStatus_createAndWait))
361 			return (SNMP_ERR_NOSUCHNAME);
362 
363 		if (param != NULL) {
364 			if (community != COMM_INITIALIZE &&
365 			    param->type == StorageType_readOnly)
366 				return (SNMP_ERR_NOT_WRITEABLE);
367 			if (param->status == RowStatus_active &&
368 			    val->v.integer != RowStatus_destroy)
369 				return (SNMP_ERR_INCONS_VALUE);
370 		}
371 
372 		switch (val->var.subs[sub - 1]) {
373 		case LEAF_snmpTargetParamsMPModel:
374 			if (val->v.integer != SNMP_MPM_SNMP_V1 &&
375 			    val->v.integer != SNMP_MPM_SNMP_V2c &&
376 			    val->v.integer != SNMP_MPM_SNMP_V3)
377 				return (SNMP_ERR_INCONS_VALUE);
378 			ctx->scratch->int1 = param->mpmodel;
379 			param->mpmodel = val->v.integer;
380 			break;
381 
382 		case LEAF_snmpTargetParamsSecurityModel:
383 			if (val->v.integer != SNMP_SECMODEL_SNMPv1 &&
384 			    val->v.integer != SNMP_SECMODEL_SNMPv2c &&
385 			    val->v.integer != SNMP_SECMODEL_USM)
386 				return (SNMP_ERR_INCONS_VALUE);
387 			ctx->scratch->int1 = param->sec_model;
388 			param->sec_model = val->v.integer;
389 			break;
390 
391 		case LEAF_snmpTargetParamsSecurityName:
392 			if (val->v.octetstring.len >= SNMP_ADM_STR32_SIZ)
393 				return (SNMP_ERR_INCONS_VALUE);
394 			ctx->scratch->int1 = strlen(param->secname) + 1;
395 			ctx->scratch->ptr1 = malloc(ctx->scratch->int1);
396 			if (ctx->scratch->ptr1 == NULL)
397 				return (SNMP_ERR_GENERR);
398 			strlcpy(ctx->scratch->ptr1, param->secname,
399 			    ctx->scratch->int1);
400 			memcpy(param->secname, val->v.octetstring.octets,
401 			    val->v.octetstring.len);
402 			param->secname[val->v.octetstring.len] = '\0';
403 			break;
404 
405 		case LEAF_snmpTargetParamsSecurityLevel:
406 			if (val->v.integer != SNMP_noAuthNoPriv &&
407 			    val->v.integer != SNMP_authNoPriv &&
408 			    val->v.integer != SNMP_authPriv)
409 				return (SNMP_ERR_INCONS_VALUE);
410 			ctx->scratch->int1 = param->sec_level;
411 			param->sec_level = val->v.integer;
412 			break;
413 
414 		case LEAF_snmpTargetParamsStorageType:
415 			return (SNMP_ERR_INCONS_VALUE);
416 
417 		case LEAF_snmpTargetParamsRowStatus:
418 			if (param != NULL) {
419 				if (val->v.integer != RowStatus_active &&
420 				    val->v.integer != RowStatus_destroy)
421 					return (SNMP_ERR_INCONS_VALUE);
422 				if (val->v.integer == RowStatus_active &&
423 				    (param->sec_model == 0 ||
424 				    param->sec_level == 0 ||
425 				    strlen(param->secname) == 0))
426 					return (SNMP_ERR_INCONS_VALUE);
427 				ctx->scratch->int1 = param->status;
428 				param->status = val->v.integer;
429 				return (SNMP_ERR_NOERROR);
430 			}
431 			if (val->v.integer != RowStatus_createAndWait ||
432 			    target_decode_index(&val->var, sub, pname) < 0)
433 				return (SNMP_ERR_INCONS_VALUE);
434 			if ((param = target_new_param(pname)) == NULL)
435 				return (SNMP_ERR_GENERR);
436 			param->status = RowStatus_destroy;
437 			if (community != COMM_INITIALIZE)
438 				param->type = StorageType_volatile;
439 			else
440 				param->type = StorageType_readOnly;
441 			break;
442 		}
443 		return (SNMP_ERR_NOERROR);
444 
445 	case SNMP_OP_COMMIT:
446 		switch (val->var.subs[sub - 1]) {
447 		case LEAF_snmpTargetParamsSecurityName:
448 			free(ctx->scratch->ptr1);
449 			break;
450 		case LEAF_snmpTargetParamsRowStatus:
451 			if ((param = target_get_param(&val->var, sub)) == NULL)
452 				return (SNMP_ERR_GENERR);
453 			if (val->v.integer == RowStatus_destroy)
454 				return (target_delete_param(param));
455 			break;
456 		default:
457 			break;
458 		}
459 		return (SNMP_ERR_NOERROR);
460 
461 	case SNMP_OP_ROLLBACK:
462 		if ((param = target_get_param(&val->var, sub)) == NULL &&
463 		    (val->var.subs[sub - 1] != LEAF_snmpTargetParamsRowStatus ||
464 		    val->v.integer != RowStatus_createAndWait))
465 			return (SNMP_ERR_GENERR);
466 		switch (val->var.subs[sub - 1]) {
467 		case LEAF_snmpTargetParamsMPModel:
468 			param->mpmodel = ctx->scratch->int1;
469 			break;
470 		case LEAF_snmpTargetParamsSecurityModel:
471 			param->sec_model = ctx->scratch->int1;
472 			break;
473 		case LEAF_snmpTargetParamsSecurityName:
474 			strlcpy(param->secname, ctx->scratch->ptr1,
475 			    sizeof(param->secname));
476 			free(ctx->scratch->ptr1);
477 			break;
478 		case LEAF_snmpTargetParamsSecurityLevel:
479 			param->sec_level = ctx->scratch->int1;
480 			break;
481 		case LEAF_snmpTargetParamsRowStatus:
482 			if (ctx->scratch->int1 == RowStatus_destroy)
483 				return (target_delete_param(param));
484 			break;
485 		default:
486 			break;
487 		}
488 
489 		return (SNMP_ERR_NOERROR);
490 
491 	default:
492 		abort();
493 	}
494 
495 	switch (val->var.subs[sub - 1]) {
496 	case LEAF_snmpTargetParamsMPModel:
497 		val->v.integer = param->mpmodel;
498 		break;
499 	case LEAF_snmpTargetParamsSecurityModel:
500 		val->v.integer = param->sec_model;
501 		break;
502 	case LEAF_snmpTargetParamsSecurityName:
503 		return (string_get(val, param->secname, -1));
504 	case LEAF_snmpTargetParamsSecurityLevel:
505 		val->v.integer = param->sec_level;
506 		break;
507 	case LEAF_snmpTargetParamsStorageType:
508 		val->v.integer = param->type;
509 		break;
510 	case LEAF_snmpTargetParamsRowStatus:
511 		val->v.integer = param->status;
512 		break;
513 	default:
514 		abort();
515 	}
516 
517 	return (SNMP_ERR_NOERROR);
518 }
519 
520 int
521 op_snmp_notify(struct snmp_context *ctx __unused, struct snmp_value *val,
522     uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
523 {
524 	char nname[SNMP_ADM_STR32_SIZ];
525 	struct target_notify *notify;
526 
527 	switch (op) {
528 	case SNMP_OP_GET:
529 		if ((notify = target_get_notify(&val->var, sub)) == NULL)
530 			return (SNMP_ERR_NOSUCHNAME);
531 		break;
532 
533 	case SNMP_OP_GETNEXT:
534 		if ((notify = target_get_next_notify(&val->var, sub)) == NULL)
535 			return (SNMP_ERR_NOSUCHNAME);
536 		target_append_index(&val->var, sub, notify->name);
537 		break;
538 
539 	case SNMP_OP_SET:
540 		if ((notify = target_get_notify(&val->var, sub)) == NULL &&
541 		    (val->var.subs[sub - 1] != LEAF_snmpNotifyRowStatus ||
542 		    val->v.integer != RowStatus_createAndGo))
543 			return (SNMP_ERR_NOSUCHNAME);
544 
545 		if (notify != NULL) {
546 			if (community != COMM_INITIALIZE &&
547 			    notify->type == StorageType_readOnly)
548 				return (SNMP_ERR_NOT_WRITEABLE);
549 		}
550 
551 		switch (val->var.subs[sub - 1]) {
552 		case LEAF_snmpNotifyTag:
553 			if (val->v.octetstring.len >= SNMP_TAG_SIZ)
554 				return (SNMP_ERR_INCONS_VALUE);
555 			ctx->scratch->int1 = strlen(notify->taglist) + 1;
556 			ctx->scratch->ptr1 = malloc(ctx->scratch->int1);
557 			if (ctx->scratch->ptr1 == NULL)
558 				return (SNMP_ERR_GENERR);
559 			strlcpy(ctx->scratch->ptr1, notify->taglist,
560 			    ctx->scratch->int1);
561 			memcpy(notify->taglist, val->v.octetstring.octets,
562 			    val->v.octetstring.len);
563 			notify->taglist[val->v.octetstring.len] = '\0';
564 			break;
565 
566 		case LEAF_snmpNotifyType:
567 			/* FALLTHROUGH */
568 		case LEAF_snmpNotifyStorageType:
569 			return (SNMP_ERR_INCONS_VALUE);
570 		case LEAF_snmpNotifyRowStatus:
571 			if (notify != NULL) {
572 				if (val->v.integer != RowStatus_active &&
573 				    val->v.integer != RowStatus_destroy)
574 					return (SNMP_ERR_INCONS_VALUE);
575 				ctx->scratch->int1 = notify->status;
576 				notify->status = val->v.integer;
577 				return (SNMP_ERR_NOERROR);
578 			}
579 			if (val->v.integer != RowStatus_createAndGo ||
580 			    target_decode_index(&val->var, sub, nname) < 0)
581 				return (SNMP_ERR_INCONS_VALUE);
582 			if ((notify = target_new_notify(nname)) == NULL)
583 				return (SNMP_ERR_GENERR);
584 			notify->status = RowStatus_destroy;
585 			if (community != COMM_INITIALIZE)
586 				notify->type = StorageType_volatile;
587 			else
588 				notify->type = StorageType_readOnly;
589 			break;
590 		}
591 		return (SNMP_ERR_NOERROR);
592 
593 	case SNMP_OP_COMMIT:
594 		switch (val->var.subs[sub - 1]) {
595 		case LEAF_snmpNotifyTag:
596 			free(ctx->scratch->ptr1);
597 			break;
598 		case LEAF_snmpNotifyRowStatus:
599 			notify = target_get_notify(&val->var, sub);
600 			if (notify == NULL)
601 				return (SNMP_ERR_GENERR);
602 			if (val->v.integer == RowStatus_destroy)
603 				return (target_delete_notify(notify));
604 			else
605 				notify->status = RowStatus_active;
606 			break;
607 		default:
608 			break;
609 		}
610 		return (SNMP_ERR_NOERROR);
611 
612 	case SNMP_OP_ROLLBACK:
613 		if ((notify = target_get_notify(&val->var, sub)) == NULL)
614 			return (SNMP_ERR_GENERR);
615 
616 		switch (val->var.subs[sub - 1]) {
617 		case LEAF_snmpNotifyTag:
618 			strlcpy(notify->taglist, ctx->scratch->ptr1,
619 			    ctx->scratch->int1);
620 			free(ctx->scratch->ptr1);
621 			break;
622 		case LEAF_snmpNotifyRowStatus:
623 			if (ctx->scratch->int1 == RowStatus_destroy)
624 				return (target_delete_notify(notify));
625 			break;
626 		default:
627 			break;
628 		}
629 		return (SNMP_ERR_NOERROR);
630 
631 	default:
632 		abort();
633 	}
634 
635 
636 	switch (val->var.subs[sub - 1]) {
637 	case LEAF_snmpNotifyTag:
638 		return (string_get(val, notify->taglist, -1));
639 	case LEAF_snmpNotifyType:
640 		val->v.integer = snmpNotifyType_trap;
641 		break;
642 	case LEAF_snmpNotifyStorageType:
643 		val->v.integer = notify->type;
644 		break;
645 	case LEAF_snmpNotifyRowStatus:
646 		val->v.integer = notify->status;
647 		break;
648 	default:
649 		abort();
650 	}
651 
652 	return (SNMP_ERR_NOERROR);
653 }
654 
655 static void
656 target_append_index(struct asn_oid *oid, uint sub, const char *name)
657 {
658 	uint32_t i;
659 
660 	oid->len = sub + strlen(name);
661 	for (i = 0; i < strlen(name); i++)
662 		oid->subs[sub + i] = name[i];
663 }
664 
665 static int
666 target_decode_index(const struct asn_oid *oid, uint sub, char *name)
667 {
668 	uint32_t i;
669 
670 	if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >=
671 	    SNMP_ADM_STR32_SIZ)
672 		return (-1);
673 
674 	for (i = 0; i < oid->subs[sub]; i++)
675 		name[i] = oid->subs[sub + i + 1];
676 	name[i] = '\0';
677 
678 	return (0);
679 }
680 
681 static struct target_address *
682 target_get_address(const struct asn_oid *oid, uint sub)
683 {
684 	char aname[SNMP_ADM_STR32_SIZ];
685 	struct target_address *addrs;
686 
687 	if (target_decode_index(oid, sub, aname) < 0)
688 		return (NULL);
689 
690 	for (addrs = target_first_address(); addrs != NULL;
691 	    addrs = target_next_address(addrs))
692 		if (strcmp(aname, addrs->name) == 0)
693 			return (addrs);
694 
695 	return (NULL);
696 }
697 
698 static struct target_address *
699 target_get_next_address(const struct asn_oid * oid, uint sub)
700 {
701 	char aname[SNMP_ADM_STR32_SIZ];
702 	struct target_address *addrs;
703 
704 	if (oid->len - sub == 0)
705 		return (target_first_address());
706 
707 	if (target_decode_index(oid, sub, aname) < 0)
708 		return (NULL);
709 
710 	for (addrs = target_first_address(); addrs != NULL;
711 	    addrs = target_next_address(addrs))
712 		if (strcmp(aname, addrs->name) == 0)
713 			return (target_next_address(addrs));
714 
715 	return (NULL);
716 }
717 
718 static struct target_param *
719 target_get_param(const struct asn_oid *oid, uint sub)
720 {
721 	char pname[SNMP_ADM_STR32_SIZ];
722 	struct target_param *param;
723 
724 	if (target_decode_index(oid, sub, pname) < 0)
725 		return (NULL);
726 
727 	for (param = target_first_param(); param != NULL;
728 	    param = target_next_param(param))
729 		if (strcmp(pname, param->name) == 0)
730 			return (param);
731 
732 	return (NULL);
733 }
734 
735 static struct target_param *
736 target_get_next_param(const struct asn_oid *oid, uint sub)
737 {
738 	char pname[SNMP_ADM_STR32_SIZ];
739 	struct target_param *param;
740 
741 	if (oid->len - sub == 0)
742 		return (target_first_param());
743 
744 	if (target_decode_index(oid, sub, pname) < 0)
745 		return (NULL);
746 
747 	for (param = target_first_param(); param != NULL;
748 	    param = target_next_param(param))
749 		if (strcmp(pname, param->name) == 0)
750 			return (target_next_param(param));
751 
752 	return (NULL);
753 }
754 
755 static struct target_notify *
756 target_get_notify(const struct asn_oid *oid, uint sub)
757 {
758 	char nname[SNMP_ADM_STR32_SIZ];
759 	struct target_notify *notify;
760 
761 	if (target_decode_index(oid, sub, nname) < 0)
762 		return (NULL);
763 
764 	for (notify = target_first_notify(); notify != NULL;
765 	    notify = target_next_notify(notify))
766 		if (strcmp(nname, notify->name) == 0)
767 			return (notify);
768 
769 	return (NULL);
770 }
771 
772 static struct target_notify *
773 target_get_next_notify(const struct asn_oid *oid, uint sub)
774 {
775 	char nname[SNMP_ADM_STR32_SIZ];
776 	struct target_notify *notify;
777 
778 	if (oid->len - sub == 0)
779 		return (target_first_notify());
780 
781 	if (target_decode_index(oid, sub, nname) < 0)
782 		return (NULL);
783 
784 	for (notify = target_first_notify(); notify != NULL;
785 	    notify = target_next_notify(notify))
786 		if (strcmp(nname, notify->name) == 0)
787 			return (target_next_notify(notify));
788 
789 	return (NULL);
790 }
791 
792 static int
793 target_init(struct lmodule *mod, int argc __unused, char *argv[] __unused)
794 {
795 	target_module = mod;
796 	target_lock = random();
797 
798 	return (0);
799 }
800 
801 
802 static int
803 target_fini(void)
804 {
805 	target_flush_all();
806 	or_unregister(reg_target);
807 	or_unregister(reg_notification);
808 
809 	return (0);
810 }
811 
812 static void
813 target_start(void)
814 {
815 	reg_target = or_register(&oid_target,
816 	    "The MIB module for managing SNMP Management Targets.",
817 	    target_module);
818 	reg_notification = or_register(&oid_notification,
819 	    "The MIB module for configuring generation of SNMP notifications.",
820 	    target_module);
821 }
822 
823 static void
824 target_dump(void)
825 {
826 	/* XXX: dump the module stats & list of mgmt targets */
827 }
828 
829 const char target_comment[] = \
830 "This module implements SNMP Management Target MIB Module defined in RFC 3413.";
831 
832 const struct snmp_module config = {
833 	.comment =	target_comment,
834 	.init =		target_init,
835 	.fini =		target_fini,
836 	.start =	target_start,
837 	.tree =		target_ctree,
838 	.dump =		target_dump,
839 	.tree_size =	target_CTREE_SIZE,
840 };
841