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
50static struct lmodule *target_module;
51/* For the registration. */
52static const struct asn_oid oid_target = OIDX_snmpTargetMIB;
53static const struct asn_oid oid_notification = OIDX_snmpNotificationMIB;
54
55static uint reg_target;
56static uint reg_notification;
57
58static int32_t target_lock;
59
60static const struct asn_oid oid_udp_domain = OIDX_snmpUDPDomain;
61
62/*
63 * Internal datastructures and forward declarations.
64 */
65static void		target_append_index(struct asn_oid *, uint,
66    const char *);
67static int		target_decode_index(const struct asn_oid *, uint,
68    char *);
69static struct target_address *target_get_address(const struct asn_oid *,
70    uint);
71static struct target_address *target_get_next_address(const struct asn_oid *,
72    uint);
73static struct target_param *target_get_param(const struct asn_oid *,
74    uint);
75static struct target_param *target_get_next_param(const struct asn_oid *,
76    uint);
77static struct target_notify *target_get_notify(const struct asn_oid *,
78    uint);
79static struct target_notify *target_get_next_notify(const struct asn_oid *,
80    uint);
81
82int
83op_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
129int
130op_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
339int
340op_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
521int
522op_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
656static void
657target_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
666static int
667target_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
682static struct target_address *
683target_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
699static struct target_address *
700target_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
719static struct target_param *
720target_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
736static struct target_param *
737target_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
756static struct target_notify *
757target_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
773static struct target_notify *
774target_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
793static int
794target_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
803static int
804target_fini(void)
805{
806	target_flush_all();
807	or_unregister(reg_target);
808	or_unregister(reg_notification);
809
810	return (0);
811}
812
813static void
814target_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
824static void
825target_dump(void)
826{
827	/* XXX: dump the module stats & list of mgmt targets */
828}
829
830static const char target_comment[] = \
831"This module implements SNMP Management Target MIB Module defined in RFC 3413.";
832
833extern const struct snmp_module config;
834const 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