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
49static struct lmodule *target_module;
50/* For the registration. */
51static const struct asn_oid oid_target = OIDX_snmpTargetMIB;
52static const struct asn_oid oid_notification = OIDX_snmpNotificationMIB;
53
54static uint reg_target;
55static uint reg_notification;
56
57static int32_t target_lock;
58
59static const struct asn_oid oid_udp_domain = OIDX_snmpUDPDomain;
60
61/*
62 * Internal datastructures and forward declarations.
63 */
64static void		target_append_index(struct asn_oid *, uint,
65    const char *);
66static int		target_decode_index(const struct asn_oid *, uint,
67    char *);
68static struct target_address *target_get_address(const struct asn_oid *,
69    uint);
70static struct target_address *target_get_next_address(const struct asn_oid *,
71    uint);
72static struct target_param *target_get_param(const struct asn_oid *,
73    uint);
74static struct target_param *target_get_next_param(const struct asn_oid *,
75    uint);
76static struct target_notify *target_get_notify(const struct asn_oid *,
77    uint);
78static struct target_notify *target_get_next_notify(const struct asn_oid *,
79    uint);
80
81int
82op_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
128int
129op_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
337int
338op_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
519int
520op_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
653static void
654target_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
663static int
664target_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
678static struct target_address *
679target_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
695static struct target_address *
696target_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
715static struct target_param *
716target_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
732static struct target_param *
733target_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
752static struct target_notify *
753target_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
769static struct target_notify *
770target_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
789static int
790target_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
799static int
800target_fini(void)
801{
802	target_flush_all();
803	or_unregister(reg_target);
804	or_unregister(reg_notification);
805
806	return (0);
807}
808
809static void
810target_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
820static void
821target_dump(void)
822{
823	/* XXX: dump the module stats & list of mgmt targets */
824}
825
826const char target_comment[] = \
827"This module implements SNMP Management Target MIB Module defined in RFC 3413.";
828
829const 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