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