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