xref: /freebsd/contrib/bsnmp/snmp_target/target_snmp.c (revision 8d20be1e22095c27faf8fe8b2f0d089739cc742e)
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 
305 	default:
306 		abort();
307 	}
308 
309 	switch (val->var.subs[sub - 1]) {
310 	case LEAF_snmpTargetAddrTDomain:
311 		return (oid_get(val, &oid_udp_domain));
312 	case LEAF_snmpTargetAddrTAddress:
313 		return (string_get(val, addrs->address, SNMP_UDP_ADDR_SIZ));
314 	case LEAF_snmpTargetAddrTimeout:
315 		val->v.integer = addrs->timeout;
316 		break;
317 	case LEAF_snmpTargetAddrRetryCount:
318 		val->v.integer = addrs->retry;
319 		break;
320 	case LEAF_snmpTargetAddrTagList:
321 		return (string_get(val, addrs->taglist, -1));
322 	case LEAF_snmpTargetAddrParams:
323 		return (string_get(val, addrs->paramname, -1));
324 	case LEAF_snmpTargetAddrStorageType:
325 		val->v.integer = addrs->type;
326 		break;
327 	case LEAF_snmpTargetAddrRowStatus:
328 		val->v.integer = addrs->status;
329 		break;
330 	default:
331 		abort();
332 	}
333 
334 	return (SNMP_ERR_NOERROR);
335 }
336 
337 int
338 op_snmp_target_params(struct snmp_context *ctx __unused, struct snmp_value *val,
339     uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
340 {
341 	char pname[SNMP_ADM_STR32_SIZ];
342 	struct target_param *param;
343 
344 	switch (op) {
345 	case SNMP_OP_GET:
346 		if ((param = target_get_param(&val->var, sub)) == NULL)
347 			return (SNMP_ERR_NOSUCHNAME);
348 		break;
349 
350 	case SNMP_OP_GETNEXT:
351 		if ((param = target_get_next_param(&val->var, sub)) == NULL)
352 			return (SNMP_ERR_NOSUCHNAME);
353 		target_append_index(&val->var, sub, param->name);
354 		break;
355 
356 	case SNMP_OP_SET:
357 		if ((param = target_get_param(&val->var, sub)) == NULL &&
358 		    (val->var.subs[sub - 1] != LEAF_snmpTargetParamsRowStatus ||
359 		    val->v.integer != RowStatus_createAndWait))
360 			return (SNMP_ERR_NOSUCHNAME);
361 
362 		if (param != NULL) {
363 			if (community != COMM_INITIALIZE &&
364 			    param->type == StorageType_readOnly)
365 				return (SNMP_ERR_NOT_WRITEABLE);
366 			if (param->status == RowStatus_active &&
367 			    val->v.integer != RowStatus_destroy)
368 				return (SNMP_ERR_INCONS_VALUE);
369 		}
370 
371 		switch (val->var.subs[sub - 1]) {
372 		case LEAF_snmpTargetParamsMPModel:
373 			if (val->v.integer != SNMP_MPM_SNMP_V1 &&
374 			    val->v.integer != SNMP_MPM_SNMP_V2c &&
375 			    val->v.integer != SNMP_MPM_SNMP_V3)
376 				return (SNMP_ERR_INCONS_VALUE);
377 			ctx->scratch->int1 = param->mpmodel;
378 			param->mpmodel = val->v.integer;
379 			break;
380 
381 		case LEAF_snmpTargetParamsSecurityModel:
382 			if (val->v.integer != SNMP_SECMODEL_SNMPv1 &&
383 			    val->v.integer != SNMP_SECMODEL_SNMPv2c &&
384 			    val->v.integer != SNMP_SECMODEL_USM)
385 				return (SNMP_ERR_INCONS_VALUE);
386 			ctx->scratch->int1 = param->sec_model;
387 			param->sec_model = val->v.integer;
388 			break;
389 
390 		case LEAF_snmpTargetParamsSecurityName:
391 			if (val->v.octetstring.len >= SNMP_ADM_STR32_SIZ)
392 				return (SNMP_ERR_INCONS_VALUE);
393 			ctx->scratch->int1 = strlen(param->secname) + 1;
394 			ctx->scratch->ptr1 = malloc(ctx->scratch->int1);
395 			if (ctx->scratch->ptr1 == NULL)
396 				return (SNMP_ERR_GENERR);
397 			strlcpy(ctx->scratch->ptr1, param->secname,
398 			    ctx->scratch->int1);
399 			memcpy(param->secname, val->v.octetstring.octets,
400 			    val->v.octetstring.len);
401 			param->secname[val->v.octetstring.len] = '\0';
402 			break;
403 
404 		case LEAF_snmpTargetParamsSecurityLevel:
405 			if (val->v.integer != SNMP_noAuthNoPriv &&
406 			    val->v.integer != SNMP_authNoPriv &&
407 			    val->v.integer != SNMP_authPriv)
408 				return (SNMP_ERR_INCONS_VALUE);
409 			ctx->scratch->int1 = param->sec_level;
410 			param->sec_level = val->v.integer;
411 			break;
412 
413 		case LEAF_snmpTargetParamsStorageType:
414 			return (SNMP_ERR_INCONS_VALUE);
415 
416 		case LEAF_snmpTargetParamsRowStatus:
417 			if (param != NULL) {
418 				if (val->v.integer != RowStatus_active &&
419 				    val->v.integer != RowStatus_destroy)
420 					return (SNMP_ERR_INCONS_VALUE);
421 				if (val->v.integer == RowStatus_active &&
422 				    (param->sec_model == 0 ||
423 				    param->sec_level == 0 ||
424 				    strlen(param->secname) == 0))
425 					return (SNMP_ERR_INCONS_VALUE);
426 				ctx->scratch->int1 = param->status;
427 				param->status = val->v.integer;
428 				return (SNMP_ERR_NOERROR);
429 			}
430 			if (val->v.integer != RowStatus_createAndWait ||
431 			    target_decode_index(&val->var, sub, pname) < 0)
432 				return (SNMP_ERR_INCONS_VALUE);
433 			if ((param = target_new_param(pname)) == NULL)
434 				return (SNMP_ERR_GENERR);
435 			param->status = RowStatus_destroy;
436 			if (community != COMM_INITIALIZE)
437 				param->type = StorageType_volatile;
438 			else
439 				param->type = StorageType_readOnly;
440 			break;
441 		}
442 		return (SNMP_ERR_NOERROR);
443 
444 	case SNMP_OP_COMMIT:
445 		switch (val->var.subs[sub - 1]) {
446 		case LEAF_snmpTargetParamsSecurityName:
447 			free(ctx->scratch->ptr1);
448 			break;
449 		case LEAF_snmpTargetParamsRowStatus:
450 			if ((param = target_get_param(&val->var, sub)) == NULL)
451 				return (SNMP_ERR_GENERR);
452 			if (val->v.integer == RowStatus_destroy)
453 				return (target_delete_param(param));
454 			break;
455 		default:
456 			break;
457 		}
458 		return (SNMP_ERR_NOERROR);
459 
460 	case SNMP_OP_ROLLBACK:
461 		if ((param = target_get_param(&val->var, sub)) == NULL &&
462 		    (val->var.subs[sub - 1] != LEAF_snmpTargetParamsRowStatus ||
463 		    val->v.integer != RowStatus_createAndWait))
464 			return (SNMP_ERR_GENERR);
465 		switch (val->var.subs[sub - 1]) {
466 		case LEAF_snmpTargetParamsMPModel:
467 			param->mpmodel = ctx->scratch->int1;
468 			break;
469 		case LEAF_snmpTargetParamsSecurityModel:
470 			param->sec_model = ctx->scratch->int1;
471 			break;
472 		case LEAF_snmpTargetParamsSecurityName:
473 			strlcpy(param->secname, ctx->scratch->ptr1,
474 			    sizeof(param->secname));
475 			free(ctx->scratch->ptr1);
476 			break;
477 		case LEAF_snmpTargetParamsSecurityLevel:
478 			param->sec_level = ctx->scratch->int1;
479 			break;
480 		case LEAF_snmpTargetParamsRowStatus:
481 			if (ctx->scratch->int1 == RowStatus_destroy)
482 				return (target_delete_param(param));
483 			break;
484 		default:
485 			break;
486 		}
487 
488 		return (SNMP_ERR_NOERROR);
489 
490 	default:
491 		abort();
492 	}
493 
494 	switch (val->var.subs[sub - 1]) {
495 	case LEAF_snmpTargetParamsMPModel:
496 		val->v.integer = param->mpmodel;
497 		break;
498 	case LEAF_snmpTargetParamsSecurityModel:
499 		val->v.integer = param->sec_model;
500 		break;
501 	case LEAF_snmpTargetParamsSecurityName:
502 		return (string_get(val, param->secname, -1));
503 	case LEAF_snmpTargetParamsSecurityLevel:
504 		val->v.integer = param->sec_level;
505 		break;
506 	case LEAF_snmpTargetParamsStorageType:
507 		val->v.integer = param->type;
508 		break;
509 	case LEAF_snmpTargetParamsRowStatus:
510 		val->v.integer = param->status;
511 		break;
512 	default:
513 		abort();
514 	}
515 
516 	return (SNMP_ERR_NOERROR);
517 }
518 
519 int
520 op_snmp_notify(struct snmp_context *ctx __unused, struct snmp_value *val,
521     uint32_t sub, uint32_t iidx __unused, enum snmp_op op)
522 {
523 	char nname[SNMP_ADM_STR32_SIZ];
524 	struct target_notify *notify;
525 
526 	switch (op) {
527 	case SNMP_OP_GET:
528 		if ((notify = target_get_notify(&val->var, sub)) == NULL)
529 			return (SNMP_ERR_NOSUCHNAME);
530 		break;
531 
532 	case SNMP_OP_GETNEXT:
533 		if ((notify = target_get_next_notify(&val->var, sub)) == NULL)
534 			return (SNMP_ERR_NOSUCHNAME);
535 		target_append_index(&val->var, sub, notify->name);
536 		break;
537 
538 	case SNMP_OP_SET:
539 		if ((notify = target_get_notify(&val->var, sub)) == NULL &&
540 		    (val->var.subs[sub - 1] != LEAF_snmpNotifyRowStatus ||
541 		    val->v.integer != RowStatus_createAndGo))
542 			return (SNMP_ERR_NOSUCHNAME);
543 
544 		if (notify != NULL) {
545 			if (community != COMM_INITIALIZE &&
546 			    notify->type == StorageType_readOnly)
547 				return (SNMP_ERR_NOT_WRITEABLE);
548 		}
549 
550 		switch (val->var.subs[sub - 1]) {
551 		case LEAF_snmpNotifyTag:
552 			if (val->v.octetstring.len >= SNMP_TAG_SIZ)
553 				return (SNMP_ERR_INCONS_VALUE);
554 			ctx->scratch->int1 = strlen(notify->taglist) + 1;
555 			ctx->scratch->ptr1 = malloc(ctx->scratch->int1);
556 			if (ctx->scratch->ptr1 == NULL)
557 				return (SNMP_ERR_GENERR);
558 			strlcpy(ctx->scratch->ptr1, notify->taglist,
559 			    ctx->scratch->int1);
560 			memcpy(notify->taglist, val->v.octetstring.octets,
561 			    val->v.octetstring.len);
562 			notify->taglist[val->v.octetstring.len] = '\0';
563 			break;
564 
565 		case LEAF_snmpNotifyType:
566 			/* FALLTHROUGH */
567 		case LEAF_snmpNotifyStorageType:
568 			return (SNMP_ERR_INCONS_VALUE);
569 		case LEAF_snmpNotifyRowStatus:
570 			if (notify != NULL) {
571 				if (val->v.integer != RowStatus_active &&
572 				    val->v.integer != RowStatus_destroy)
573 					return (SNMP_ERR_INCONS_VALUE);
574 				ctx->scratch->int1 = notify->status;
575 				notify->status = val->v.integer;
576 				return (SNMP_ERR_NOERROR);
577 			}
578 			if (val->v.integer != RowStatus_createAndGo ||
579 			    target_decode_index(&val->var, sub, nname) < 0)
580 				return (SNMP_ERR_INCONS_VALUE);
581 			if ((notify = target_new_notify(nname)) == NULL)
582 				return (SNMP_ERR_GENERR);
583 			notify->status = RowStatus_destroy;
584 			if (community != COMM_INITIALIZE)
585 				notify->type = StorageType_volatile;
586 			else
587 				notify->type = StorageType_readOnly;
588 			break;
589 		}
590 		return (SNMP_ERR_NOERROR);
591 
592 	case SNMP_OP_COMMIT:
593 		switch (val->var.subs[sub - 1]) {
594 		case LEAF_snmpNotifyTag:
595 			free(ctx->scratch->ptr1);
596 			break;
597 		case LEAF_snmpNotifyRowStatus:
598 			notify = target_get_notify(&val->var, sub);
599 			if (notify == NULL)
600 				return (SNMP_ERR_GENERR);
601 			if (val->v.integer == RowStatus_destroy)
602 				return (target_delete_notify(notify));
603 			else
604 				notify->status = RowStatus_active;
605 			break;
606 		default:
607 			break;
608 		}
609 		return (SNMP_ERR_NOERROR);
610 
611 	case SNMP_OP_ROLLBACK:
612 		if ((notify = target_get_notify(&val->var, sub)) == NULL)
613 			return (SNMP_ERR_GENERR);
614 
615 		switch (val->var.subs[sub - 1]) {
616 		case LEAF_snmpNotifyTag:
617 			strlcpy(notify->taglist, ctx->scratch->ptr1,
618 			    ctx->scratch->int1);
619 			free(ctx->scratch->ptr1);
620 			break;
621 		case LEAF_snmpNotifyRowStatus:
622 			if (ctx->scratch->int1 == RowStatus_destroy)
623 				return (target_delete_notify(notify));
624 			break;
625 		default:
626 			break;
627 		}
628 
629 	default:
630 		abort();
631 	}
632 
633 
634 	switch (val->var.subs[sub - 1]) {
635 	case LEAF_snmpNotifyTag:
636 		return (string_get(val, notify->taglist, -1));
637 	case LEAF_snmpNotifyType:
638 		val->v.integer = snmpNotifyType_trap;
639 		break;
640 	case LEAF_snmpNotifyStorageType:
641 		val->v.integer = notify->type;
642 		break;
643 	case LEAF_snmpNotifyRowStatus:
644 		val->v.integer = notify->status;
645 		break;
646 	default:
647 		abort();
648 	}
649 
650 	return (SNMP_ERR_NOERROR);
651 }
652 
653 static void
654 target_append_index(struct asn_oid *oid, uint sub, const char *name)
655 {
656 	uint32_t i;
657 
658 	oid->len = sub + strlen(name);
659 	for (i = 0; i < strlen(name); i++)
660 		oid->subs[sub + i] = name[i];
661 }
662 
663 static int
664 target_decode_index(const struct asn_oid *oid, uint sub, char *name)
665 {
666 	uint32_t i, len;
667 
668 	if ((len = oid->len - sub) >= SNMP_ADM_STR32_SIZ)
669 		return (-1);
670 
671 	for (i = 0; i < len; i++)
672 		name[i] = oid->subs[sub + i];
673 	name[i] = '\0';
674 
675 	return (0);
676 }
677 
678 static struct target_address *
679 target_get_address(const struct asn_oid *oid, uint sub)
680 {
681 	char aname[SNMP_ADM_STR32_SIZ];
682 	struct target_address *addrs;
683 
684 	if (target_decode_index(oid, sub, aname) < 0)
685 		return (NULL);
686 
687 	for (addrs = target_first_address(); addrs != NULL;
688 	    addrs = target_next_address(addrs))
689 		if (strcmp(aname, addrs->name) == 0)
690 			return (addrs);
691 
692 	return (NULL);
693 }
694 
695 static struct target_address *
696 target_get_next_address(const struct asn_oid * oid, uint sub)
697 {
698 	char aname[SNMP_ADM_STR32_SIZ];
699 	struct target_address *addrs;
700 
701 	if (oid->len - sub == 0)
702 		return (target_first_address());
703 
704 	if (target_decode_index(oid, sub, aname) < 0)
705 		return (NULL);
706 
707 	for (addrs = target_first_address(); addrs != NULL;
708 	    addrs = target_next_address(addrs))
709 		if (strcmp(aname, addrs->name) == 0)
710 			return (target_next_address(addrs));
711 
712 	return (NULL);
713 }
714 
715 static struct target_param *
716 target_get_param(const struct asn_oid *oid, uint sub)
717 {
718 	char pname[SNMP_ADM_STR32_SIZ];
719 	struct target_param *param;
720 
721 	if (target_decode_index(oid, sub, pname) < 0)
722 		return (NULL);
723 
724 	for (param = target_first_param(); param != NULL;
725 	    param = target_next_param(param))
726 		if (strcmp(pname, param->name) == 0)
727 			return (param);
728 
729 	return (NULL);
730 }
731 
732 static struct target_param *
733 target_get_next_param(const struct asn_oid *oid, uint sub)
734 {
735 	char pname[SNMP_ADM_STR32_SIZ];
736 	struct target_param *param;
737 
738 	if (oid->len - sub == 0)
739 		return (target_first_param());
740 
741 	if (target_decode_index(oid, sub, pname) < 0)
742 		return (NULL);
743 
744 	for (param = target_first_param(); param != NULL;
745 	    param = target_next_param(param))
746 		if (strcmp(pname, param->name) == 0)
747 			return (target_next_param(param));
748 
749 	return (NULL);
750 }
751 
752 static struct target_notify *
753 target_get_notify(const struct asn_oid *oid, uint sub)
754 {
755 	char nname[SNMP_ADM_STR32_SIZ];
756 	struct target_notify *notify;
757 
758 	if (target_decode_index(oid, sub, nname) < 0)
759 		return (NULL);
760 
761 	for (notify = target_first_notify(); notify != NULL;
762 	    notify = target_next_notify(notify))
763 		if (strcmp(nname, notify->name) == 0)
764 			return (notify);
765 
766 	return (NULL);
767 }
768 
769 static struct target_notify *
770 target_get_next_notify(const struct asn_oid *oid, uint sub)
771 {
772 	char nname[SNMP_ADM_STR32_SIZ];
773 	struct target_notify *notify;
774 
775 	if (oid->len - sub == 0)
776 		return (target_first_notify());
777 
778 	if (target_decode_index(oid, sub, nname) < 0)
779 		return (NULL);
780 
781 	for (notify = target_first_notify(); notify != NULL;
782 	    notify = target_next_notify(notify))
783 		if (strcmp(nname, notify->name) == 0)
784 			return (target_next_notify(notify));
785 
786 	return (NULL);
787 }
788 
789 static int
790 target_init(struct lmodule *mod, int argc __unused, char *argv[] __unused)
791 {
792 	target_module = mod;
793 	target_lock = random();
794 
795 	return (0);
796 }
797 
798 
799 static int
800 target_fini(void)
801 {
802 	target_flush_all();
803 	or_unregister(reg_target);
804 	or_unregister(reg_notification);
805 
806 	return (0);
807 }
808 
809 static void
810 target_start(void)
811 {
812 	reg_target = or_register(&oid_target,
813 	    "The MIB module for managing SNMP Management Targets.",
814 	    target_module);
815 	reg_notification = or_register(&oid_notification,
816 	    "The MIB module for configuring generation of SNMP notifications.",
817 	    target_module);
818 }
819 
820 static void
821 target_dump(void)
822 {
823 	/* XXX: dump the module stats & list of mgmt targets */
824 }
825 
826 const char target_comment[] = \
827 "This module implements SNMP Management Target MIB Module defined in RFC 3413.";
828 
829 const struct snmp_module config = {
830 	.comment =	target_comment,
831 	.init =		target_init,
832 	.fini =		target_fini,
833 	.start =	target_start,
834 	.tree =		target_ctree,
835 	.dump =		target_dump,
836 	.tree_size =	target_CTREE_SIZE,
837 };
838