action.c revision 310572
1/*
2 * Copyright (c) 2001-2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 *	All rights reserved.
5 * Copyright (c) 2004-2006
6 *	Hartmut Brandt.
7 *	All rights reserved.
8 *
9 * Author: Harti Brandt <harti@freebsd.org>
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $Begemot: action.c 517 2006-10-31 08:52:04Z brandt_h $
33 *
34 * Variable access for SNMPd
35 */
36#include <sys/types.h>
37#include <sys/queue.h>
38#include <sys/sysctl.h>
39#include <sys/un.h>
40#include <sys/utsname.h>
41#include <ctype.h>
42#include <errno.h>
43#include <stdarg.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <syslog.h>
48
49#include "snmpmod.h"
50#include "snmpd.h"
51#include "tree.h"
52#include "oid.h"
53
54static const struct asn_oid
55	oid_begemotSnmpdModuleTable = OIDX_begemotSnmpdModuleTable;
56
57#ifdef __FreeBSD__
58static const struct asn_oid
59	oid_freeBSDVersion = OIDX_freeBSDVersion;
60#endif
61
62/*
63 * Get an integer value from the KERN sysctl subtree.
64 */
65static char *
66act_getkernint(int id)
67{
68	int mib[2];
69	size_t len;
70	u_long value;
71	char *string;
72
73	mib[0] = CTL_KERN;
74	mib[1] = id;
75	len = sizeof(value);
76	if (sysctl(mib, 2, &value, &len, NULL, 0) != 0)
77		return (NULL);
78
79	if ((string = malloc(20)) == NULL)
80		return (NULL);
81	sprintf(string, "%lu", value);
82	return (string);
83}
84
85/*
86 * Initialize global variables of the system group.
87 */
88int
89init_actvals(void)
90{
91	struct utsname uts;
92	char *hostid;
93	size_t len;
94#ifdef __FreeBSD__
95	char *rel, *p, *end;
96	u_long num;
97#endif
98
99	if (uname(&uts) == -1)
100		return (-1);
101
102	if ((systemg.name = strdup(uts.nodename)) == NULL)
103		return (-1);
104
105	if ((hostid = act_getkernint(KERN_HOSTID)) == NULL)
106		return (-1);
107
108	len = strlen(uts.nodename) + 1;
109	len += strlen(hostid) + 1;
110	len += strlen(uts.sysname) + 1;
111	len += strlen(uts.release) + 1;
112
113	if ((systemg.descr = malloc(len)) == NULL) {
114		free(hostid);
115		return (-1);
116	}
117	sprintf(systemg.descr, "%s %s %s %s", uts.nodename, hostid, uts.sysname,
118	    uts.release);
119
120#ifdef __FreeBSD__
121	/*
122	 * Construct a FreeBSD oid
123	 */
124	systemg.object_id = oid_freeBSDVersion;
125	rel = uts.release;
126	while ((p = strsep(&rel, ".")) != NULL &&
127	    systemg.object_id.len < ASN_MAXOIDLEN) {
128		systemg.object_id.subs[systemg.object_id.len] = 0;
129		if (*p != '\0') {
130			num = strtoul(p, &end, 10);
131			if (end == p)
132				break;
133			systemg.object_id.subs[systemg.object_id.len] = num;
134		}
135		systemg.object_id.len++;
136	}
137#endif
138
139	free(hostid);
140
141	return (0);
142}
143
144/*
145 * Initialize global variables of the snmpEngine group.
146 */
147int
148init_snmpd_engine(void)
149{
150	char *hostid;
151
152	snmpd_engine.engine_boots = 1;
153	snmpd_engine.engine_time = 1;
154	snmpd_engine.max_msg_size = 1500; /* XXX */
155
156	snmpd_engine.engine_id[0] = ((OID_freeBSD & 0xff000000) >> 24) | 0x80;
157	snmpd_engine.engine_id[1] = (OID_freeBSD & 0xff0000) >> 16;
158	snmpd_engine.engine_id[2] = (OID_freeBSD & 0xff00) >> 8;
159	snmpd_engine.engine_id[3] = OID_freeBSD & 0xff;
160	snmpd_engine.engine_id[4] = 128;
161	snmpd_engine.engine_len = 5;
162
163	if ((hostid = act_getkernint(KERN_HOSTID)) == NULL)
164		return (-1);
165
166	if (strlen(hostid) > SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len) {
167		memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len,
168		    hostid, SNMP_ENGINE_ID_SIZ - snmpd_engine.engine_len);
169		snmpd_engine.engine_len = SNMP_ENGINE_ID_SIZ;
170	} else {
171		memcpy(snmpd_engine.engine_id + snmpd_engine.engine_len,
172		    hostid, strlen(hostid));
173		snmpd_engine.engine_len += strlen(hostid);
174	}
175
176	free(hostid);
177
178	return (0);
179}
180
181int
182set_snmpd_engine(void)
183{
184	FILE *fp;
185	uint32_t i;
186	uint8_t *cptr, engine[2 * SNMP_ENGINE_ID_SIZ + 2];
187	uint8_t myengine[2 * SNMP_ENGINE_ID_SIZ + 2];
188
189	if (engine_file[0] == '\0')
190		return (-1);
191
192	cptr = myengine;
193	for (i = 0; i < snmpd_engine.engine_len; i++)
194		cptr += sprintf(cptr, "%.2x", snmpd_engine.engine_id[i]);
195	*cptr++ = '\n';
196	*cptr++ = '\0';
197
198	if ((fp = fopen(engine_file, "r+")) != NULL) {
199		if (fgets(engine, sizeof(engine) - 1, fp) == NULL ||
200		    fscanf(fp, "%u",  &snmpd_engine.engine_boots) <= 0) {
201			fclose(fp);
202			goto save_boots;
203		}
204
205		fclose(fp);
206		if (strcmp(myengine, engine) != 0)
207			snmpd_engine.engine_boots = 1;
208		else
209			snmpd_engine.engine_boots++;
210	} else if (errno != ENOENT)
211		return (-1);
212
213save_boots:
214	if ((fp = fopen(engine_file, "w+")) == NULL)
215		return (-1);
216	fprintf(fp, "%s%u\n", myengine, snmpd_engine.engine_boots);
217	fclose(fp);
218
219	return (0);
220}
221
222/*************************************************************
223 *
224 * System group
225 */
226int
227op_system_group(struct snmp_context *ctx, struct snmp_value *value,
228    u_int sub, u_int iidx __unused, enum snmp_op op)
229{
230	asn_subid_t which = value->var.subs[sub - 1];
231
232	switch (op) {
233
234	  case SNMP_OP_GETNEXT:
235		abort();
236
237	  case SNMP_OP_GET:
238		break;
239
240	  case SNMP_OP_SET:
241		switch (which) {
242
243		  case LEAF_sysDescr:
244			if (community != COMM_INITIALIZE)
245				return (SNMP_ERR_NOT_WRITEABLE);
246			return (string_save(value, ctx, -1, &systemg.descr));
247
248		  case LEAF_sysObjectId:
249			if (community != COMM_INITIALIZE)
250				return (SNMP_ERR_NOT_WRITEABLE);
251			return (oid_save(value, ctx, &systemg.object_id));
252
253		  case LEAF_sysContact:
254			return (string_save(value, ctx, -1, &systemg.contact));
255
256		  case LEAF_sysName:
257			return (string_save(value, ctx, -1, &systemg.name));
258
259		  case LEAF_sysLocation:
260			return (string_save(value, ctx, -1, &systemg.location));
261		}
262		return (SNMP_ERR_NO_CREATION);
263
264	  case SNMP_OP_ROLLBACK:
265		switch (which) {
266
267		  case LEAF_sysDescr:
268			string_rollback(ctx, &systemg.descr);
269			return (SNMP_ERR_NOERROR);
270		  case LEAF_sysObjectId:
271			oid_rollback(ctx, &systemg.object_id);
272			return (SNMP_ERR_NOERROR);
273		  case LEAF_sysContact:
274			string_rollback(ctx, &systemg.contact);
275			return (SNMP_ERR_NOERROR);
276		  case LEAF_sysName:
277			string_rollback(ctx, &systemg.name);
278			return (SNMP_ERR_NOERROR);
279		  case LEAF_sysLocation:
280			string_rollback(ctx, &systemg.location);
281			return (SNMP_ERR_NOERROR);
282		}
283		abort();
284
285	  case SNMP_OP_COMMIT:
286		switch (which) {
287
288		  case LEAF_sysDescr:
289			string_commit(ctx);
290			return (SNMP_ERR_NOERROR);
291		  case LEAF_sysObjectId:
292			oid_commit(ctx);
293			return (SNMP_ERR_NOERROR);
294		  case LEAF_sysContact:
295			string_commit(ctx);
296			return (SNMP_ERR_NOERROR);
297		  case LEAF_sysName:
298			string_commit(ctx);
299			return (SNMP_ERR_NOERROR);
300		  case LEAF_sysLocation:
301			string_commit(ctx);
302			return (SNMP_ERR_NOERROR);
303		}
304		abort();
305	}
306
307	/*
308	 * Come here for GET.
309	 */
310	switch (which) {
311
312	  case LEAF_sysDescr:
313		return (string_get(value, systemg.descr, -1));
314	  case LEAF_sysObjectId:
315		return (oid_get(value, &systemg.object_id));
316	  case LEAF_sysUpTime:
317		value->v.uint32 = get_ticks() - start_tick;
318		break;
319	  case LEAF_sysContact:
320		return (string_get(value, systemg.contact, -1));
321	  case LEAF_sysName:
322		return (string_get(value, systemg.name, -1));
323	  case LEAF_sysLocation:
324		return (string_get(value, systemg.location, -1));
325	  case LEAF_sysServices:
326		value->v.integer = systemg.services;
327		break;
328	  case LEAF_sysORLastChange:
329		value->v.uint32 = systemg.or_last_change;
330		break;
331	}
332	return (SNMP_ERR_NOERROR);
333}
334
335/*************************************************************
336 *
337 * Debug group
338 */
339int
340op_debug(struct snmp_context *ctx, struct snmp_value *value, u_int sub,
341    u_int iidx __unused, enum snmp_op op)
342{
343	asn_subid_t which = value->var.subs[sub - 1];
344
345	switch (op) {
346
347	  case SNMP_OP_GETNEXT:
348		abort();
349
350	  case SNMP_OP_GET:
351		switch (which) {
352
353		  case LEAF_begemotSnmpdDebugDumpPdus:
354			value->v.integer = TRUTH_MK(debug.dump_pdus);
355			break;
356
357		  case LEAF_begemotSnmpdDebugSnmpTrace:
358			value->v.uint32 = snmp_trace;
359			break;
360
361		  case LEAF_begemotSnmpdDebugSyslogPri:
362			value->v.integer = debug.logpri;
363			break;
364		}
365		return (SNMP_ERR_NOERROR);
366
367	  case SNMP_OP_SET:
368		switch (which) {
369
370		  case LEAF_begemotSnmpdDebugDumpPdus:
371			if (!TRUTH_OK(value->v.integer))
372				return (SNMP_ERR_WRONG_VALUE);
373			ctx->scratch->int1 = debug.dump_pdus;
374			debug.dump_pdus = TRUTH_GET(value->v.integer);
375			return (SNMP_ERR_NOERROR);
376
377		  case LEAF_begemotSnmpdDebugSnmpTrace:
378			ctx->scratch->int1 = snmp_trace;
379			snmp_trace = value->v.uint32;
380			return (SNMP_ERR_NOERROR);
381
382		  case LEAF_begemotSnmpdDebugSyslogPri:
383			if (value->v.integer < 0 || value->v.integer > 8)
384				return (SNMP_ERR_WRONG_VALUE);
385			ctx->scratch->int1 = debug.logpri;
386			debug.logpri = (u_int)value->v.integer;
387			return (SNMP_ERR_NOERROR);
388		}
389		return (SNMP_ERR_NO_CREATION);
390
391	  case SNMP_OP_ROLLBACK:
392		switch (which) {
393
394		  case LEAF_begemotSnmpdDebugDumpPdus:
395			debug.dump_pdus = ctx->scratch->int1;
396			return (SNMP_ERR_NOERROR);
397
398		  case LEAF_begemotSnmpdDebugSnmpTrace:
399			snmp_trace = ctx->scratch->int1;
400			return (SNMP_ERR_NOERROR);
401
402		  case LEAF_begemotSnmpdDebugSyslogPri:
403			debug.logpri = ctx->scratch->int1;
404			return (SNMP_ERR_NOERROR);
405		}
406		abort();
407
408	  case SNMP_OP_COMMIT:
409		switch (which) {
410
411		  case LEAF_begemotSnmpdDebugDumpPdus:
412		  case LEAF_begemotSnmpdDebugSnmpTrace:
413			return (SNMP_ERR_NOERROR);
414
415		  case LEAF_begemotSnmpdDebugSyslogPri:
416			if (debug.logpri == 0)
417				setlogmask(0);
418			else
419				setlogmask(LOG_UPTO(debug.logpri - 1));
420			return (SNMP_ERR_NOERROR);
421		}
422		abort();
423	}
424	abort();
425}
426
427/*************************************************************
428 *
429 * OR Table
430 */
431int
432op_or_table(struct snmp_context *ctx __unused, struct snmp_value *value,
433    u_int sub, u_int iidx __unused, enum snmp_op op)
434{
435	struct objres *objres;
436
437	switch (op) {
438
439	  case SNMP_OP_GETNEXT:
440		if ((objres = NEXT_OBJECT_INT(&objres_list, &value->var, sub))
441		    == NULL)
442			return (SNMP_ERR_NOSUCHNAME);
443		value->var.subs[sub] = objres->index;
444		value->var.len = sub + 1;
445		break;
446
447	  case SNMP_OP_GET:
448		if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub))
449		    == NULL)
450			return (SNMP_ERR_NOSUCHNAME);
451		break;
452
453	  case SNMP_OP_SET:
454		if ((objres = FIND_OBJECT_INT(&objres_list, &value->var, sub))
455		    == NULL)
456			return (SNMP_ERR_NO_CREATION);
457		return (SNMP_ERR_NOT_WRITEABLE);
458
459	  case SNMP_OP_ROLLBACK:
460	  case SNMP_OP_COMMIT:
461	  default:
462		abort();
463	}
464
465	/*
466	 * Come here for GET, GETNEXT.
467	 */
468	switch (value->var.subs[sub - 1]) {
469
470	  case LEAF_sysORID:
471		value->v.oid = objres->oid;
472		break;
473
474	  case LEAF_sysORDescr:
475		return (string_get(value, objres->descr, -1));
476
477	  case LEAF_sysORUpTime:
478		value->v.uint32 = objres->uptime;
479		break;
480	}
481	return (SNMP_ERR_NOERROR);
482}
483
484/*************************************************************
485 *
486 * mib-2 snmp
487 */
488int
489op_snmp(struct snmp_context *ctx, struct snmp_value *value,
490    u_int sub, u_int iidx __unused, enum snmp_op op)
491{
492	switch (op) {
493
494	  case SNMP_OP_GETNEXT:
495		abort();
496
497	  case SNMP_OP_GET:
498		switch (value->var.subs[sub - 1]) {
499
500		  case LEAF_snmpInPkts:
501			value->v.uint32 = snmpd_stats.inPkts;
502			break;
503
504		  case LEAF_snmpInBadVersions:
505			value->v.uint32 = snmpd_stats.inBadVersions;
506			break;
507
508		  case LEAF_snmpInBadCommunityNames:
509			value->v.uint32 = snmpd_stats.inBadCommunityNames;
510			break;
511
512		  case LEAF_snmpInBadCommunityUses:
513			value->v.uint32 = snmpd_stats.inBadCommunityUses;
514			break;
515
516		  case LEAF_snmpInASNParseErrs:
517			value->v.uint32 = snmpd_stats.inASNParseErrs;
518			break;
519
520		  case LEAF_snmpEnableAuthenTraps:
521			value->v.integer = TRUTH_MK(snmpd.auth_traps);
522			break;
523
524		  case LEAF_snmpSilentDrops:
525			value->v.uint32 = snmpd_stats.silentDrops;
526			break;
527
528		  case LEAF_snmpProxyDrops:
529			value->v.uint32 = snmpd_stats.proxyDrops;
530			break;
531
532		  default:
533			return (SNMP_ERR_NOSUCHNAME);
534
535		}
536		return (SNMP_ERR_NOERROR);
537
538	  case SNMP_OP_SET:
539		switch (value->var.subs[sub - 1]) {
540		  case LEAF_snmpEnableAuthenTraps:
541			if (!TRUTH_OK(value->v.integer))
542				return (SNMP_ERR_WRONG_VALUE);
543			ctx->scratch->int1 = value->v.integer;
544			snmpd.auth_traps = TRUTH_GET(value->v.integer);
545			return (SNMP_ERR_NOERROR);
546		}
547		abort();
548
549	  case SNMP_OP_ROLLBACK:
550		switch (value->var.subs[sub - 1]) {
551		  case LEAF_snmpEnableAuthenTraps:
552			snmpd.auth_traps = ctx->scratch->int1;
553			return (SNMP_ERR_NOERROR);
554		}
555		abort();
556
557	  case SNMP_OP_COMMIT:
558		switch (value->var.subs[sub - 1]) {
559		  case LEAF_snmpEnableAuthenTraps:
560			return (SNMP_ERR_NOERROR);
561		}
562		abort();
563	}
564	abort();
565}
566
567/*************************************************************
568 *
569 * SNMPd statistics group
570 */
571int
572op_snmpd_stats(struct snmp_context *ctx __unused, struct snmp_value *value,
573    u_int sub, u_int iidx __unused, enum snmp_op op)
574{
575	switch (op) {
576
577	  case SNMP_OP_GET:
578		switch (value->var.subs[sub - 1]) {
579
580		  case LEAF_begemotSnmpdStatsNoRxBufs:
581			value->v.uint32 = snmpd_stats.noRxbuf;
582			break;
583
584		  case LEAF_begemotSnmpdStatsNoTxBufs:
585			value->v.uint32 = snmpd_stats.noTxbuf;
586			break;
587
588		  case LEAF_begemotSnmpdStatsInTooLongPkts:
589			value->v.uint32 = snmpd_stats.inTooLong;
590			break;
591
592		  case LEAF_begemotSnmpdStatsInBadPduTypes:
593			value->v.uint32 = snmpd_stats.inBadPduTypes;
594			break;
595
596		  default:
597			return (SNMP_ERR_NOSUCHNAME);
598		}
599		return (SNMP_ERR_NOERROR);
600
601	  case SNMP_OP_SET:
602	  case SNMP_OP_ROLLBACK:
603	  case SNMP_OP_COMMIT:
604	  case SNMP_OP_GETNEXT:
605		abort();
606	}
607	abort();
608}
609
610/*
611 * SNMPd configuration scalars
612 */
613int
614op_snmpd_config(struct snmp_context *ctx, struct snmp_value *value,
615    u_int sub, u_int iidx __unused, enum snmp_op op)
616{
617	asn_subid_t which = value->var.subs[sub - 1];
618
619	switch (op) {
620
621	  case SNMP_OP_GETNEXT:
622		abort();
623
624	  case SNMP_OP_GET:
625		switch (which) {
626
627		  case LEAF_begemotSnmpdTransmitBuffer:
628			value->v.integer = snmpd.txbuf;
629			break;
630		  case LEAF_begemotSnmpdReceiveBuffer:
631			value->v.integer = snmpd.rxbuf;
632			break;
633		  case LEAF_begemotSnmpdCommunityDisable:
634			value->v.integer = TRUTH_MK(snmpd.comm_dis);
635			break;
636		  case LEAF_begemotSnmpdTrap1Addr:
637			return (ip_get(value, snmpd.trap1addr));
638		  case LEAF_begemotSnmpdVersionEnable:
639			value->v.uint32 = snmpd.version_enable;
640			break;
641		  default:
642			return (SNMP_ERR_NOSUCHNAME);
643		}
644		return (SNMP_ERR_NOERROR);
645
646	  case SNMP_OP_SET:
647		switch (which) {
648
649		  case LEAF_begemotSnmpdTransmitBuffer:
650			ctx->scratch->int1 = snmpd.txbuf;
651			if (value->v.integer < 484 ||
652			    value->v.integer > 65535)
653				return (SNMP_ERR_WRONG_VALUE);
654			snmpd.txbuf = value->v.integer;
655			return (SNMP_ERR_NOERROR);
656
657		  case LEAF_begemotSnmpdReceiveBuffer:
658			ctx->scratch->int1 = snmpd.rxbuf;
659			if (value->v.integer < 484 ||
660			    value->v.integer > 65535)
661				return (SNMP_ERR_WRONG_VALUE);
662			snmpd.rxbuf = value->v.integer;
663			return (SNMP_ERR_NOERROR);
664
665		  case LEAF_begemotSnmpdCommunityDisable:
666			ctx->scratch->int1 = snmpd.comm_dis;
667			if (!TRUTH_OK(value->v.integer))
668				return (SNMP_ERR_WRONG_VALUE);
669			if (TRUTH_GET(value->v.integer)) {
670				snmpd.comm_dis = 1;
671			} else {
672				if (snmpd.comm_dis)
673					return (SNMP_ERR_WRONG_VALUE);
674			}
675			return (SNMP_ERR_NOERROR);
676
677		  case LEAF_begemotSnmpdTrap1Addr:
678			return (ip_save(value, ctx, snmpd.trap1addr));
679
680		  case LEAF_begemotSnmpdVersionEnable:
681			if (community != COMM_INITIALIZE)
682				return (SNMP_ERR_NOT_WRITEABLE);
683			ctx->scratch->int1 = snmpd.version_enable;
684			if (value->v.uint32 == 0 ||
685			    (value->v.uint32 & ~VERS_ENABLE_ALL))
686				return (SNMP_ERR_WRONG_VALUE);
687			snmpd.version_enable = value->v.uint32;
688			return (SNMP_ERR_NOERROR);
689		}
690		abort();
691
692	  case SNMP_OP_ROLLBACK:
693		switch (which) {
694
695		  case LEAF_begemotSnmpdTransmitBuffer:
696			snmpd.rxbuf = ctx->scratch->int1;
697			return (SNMP_ERR_NOERROR);
698		  case LEAF_begemotSnmpdReceiveBuffer:
699			snmpd.txbuf = ctx->scratch->int1;
700			return (SNMP_ERR_NOERROR);
701		  case LEAF_begemotSnmpdCommunityDisable:
702			snmpd.comm_dis = ctx->scratch->int1;
703			return (SNMP_ERR_NOERROR);
704		  case LEAF_begemotSnmpdTrap1Addr:
705			ip_rollback(ctx, snmpd.trap1addr);
706			return (SNMP_ERR_NOERROR);
707		  case LEAF_begemotSnmpdVersionEnable:
708			snmpd.version_enable = ctx->scratch->int1;
709			return (SNMP_ERR_NOERROR);
710		}
711		abort();
712
713	  case SNMP_OP_COMMIT:
714		switch (which) {
715
716		  case LEAF_begemotSnmpdTransmitBuffer:
717		  case LEAF_begemotSnmpdReceiveBuffer:
718		  case LEAF_begemotSnmpdCommunityDisable:
719			return (SNMP_ERR_NOERROR);
720		  case LEAF_begemotSnmpdTrap1Addr:
721			ip_commit(ctx);
722			return (SNMP_ERR_NOERROR);
723		  case LEAF_begemotSnmpdVersionEnable:
724			return (SNMP_ERR_NOERROR);
725		}
726		abort();
727	}
728	abort();
729}
730
731/*
732 * The community table
733 */
734int
735op_community(struct snmp_context *ctx, struct snmp_value *value,
736    u_int sub, u_int iidx __unused, enum snmp_op op)
737{
738	asn_subid_t which = value->var.subs[sub - 1];
739	struct community *c;
740
741	switch (op) {
742
743	  case SNMP_OP_GETNEXT:
744		if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
745		    (c = NEXT_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
746			return (SNMP_ERR_NOSUCHNAME);
747		index_append(&value->var, sub, &c->index);
748		break;
749
750	  case SNMP_OP_GET:
751		if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
752		    (c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
753			return (SNMP_ERR_NOSUCHNAME);
754		break;
755
756	  case SNMP_OP_SET:
757		if ((community != COMM_INITIALIZE && snmpd.comm_dis) ||
758		    (c = FIND_OBJECT_OID(&community_list, &value->var, sub)) == NULL)
759			return (SNMP_ERR_NO_CREATION);
760		if (which != LEAF_begemotSnmpdCommunityString)
761			return (SNMP_ERR_NOT_WRITEABLE);
762		return (string_save(value, ctx, -1, &c->string));
763
764	  case SNMP_OP_ROLLBACK:
765		if (which == LEAF_begemotSnmpdCommunityString) {
766			if ((c = FIND_OBJECT_OID(&community_list, &value->var,
767			    sub)) == NULL)
768				string_free(ctx);
769			else
770				string_rollback(ctx, &c->string);
771			return (SNMP_ERR_NOERROR);
772		}
773		abort();
774
775	  case SNMP_OP_COMMIT:
776		if (which == LEAF_begemotSnmpdCommunityString) {
777			if ((c = FIND_OBJECT_OID(&community_list, &value->var,
778			    sub)) == NULL)
779				string_free(ctx);
780			else
781				string_commit(ctx);
782			return (SNMP_ERR_NOERROR);
783		}
784		abort();
785
786	  default:
787		abort();
788	}
789
790	switch (which) {
791
792	  case LEAF_begemotSnmpdCommunityString:
793		return (string_get(value, c->string, -1));
794
795	  case LEAF_begemotSnmpdCommunityDescr:
796		return (string_get(value, c->descr, -1));
797	}
798	abort();
799}
800
801/*
802 * Module table.
803 */
804struct module_dep {
805	struct snmp_dependency dep;
806	u_char	section[LM_SECTION_MAX + 1];
807	u_char	*path;
808	struct lmodule *m;
809};
810
811static int
812dep_modules(struct snmp_context *ctx, struct snmp_dependency *dep,
813    enum snmp_depop op)
814{
815	struct module_dep *mdep = (struct module_dep *)(void *)dep;
816
817	switch (op) {
818
819	  case SNMP_DEPOP_COMMIT:
820		if (mdep->path == NULL) {
821			/* unload - find the module */
822			TAILQ_FOREACH(mdep->m, &lmodules, link)
823				if (strcmp(mdep->m->section,
824				    mdep->section) == 0)
825					break;
826			if (mdep->m == NULL)
827				/* no such module - that's ok */
828				return (SNMP_ERR_NOERROR);
829
830			/* handle unloading in the finalizer */
831			return (SNMP_ERR_NOERROR);
832		}
833		/* load */
834		if ((mdep->m = lm_load(mdep->path, mdep->section)) == NULL) {
835			/* could not load */
836			return (SNMP_ERR_RES_UNAVAIL);
837		}
838		/* start in finalizer */
839		return (SNMP_ERR_NOERROR);
840
841	  case SNMP_DEPOP_ROLLBACK:
842		if (mdep->path == NULL) {
843			/* rollback unload - the finalizer takes care */
844			return (SNMP_ERR_NOERROR);
845		}
846		/* rollback load */
847		lm_unload(mdep->m);
848		return (SNMP_ERR_NOERROR);
849
850	  case SNMP_DEPOP_FINISH:
851		if (mdep->path == NULL) {
852			if (mdep->m != NULL && ctx->code == SNMP_RET_OK)
853				lm_unload(mdep->m);
854		} else {
855			if (mdep->m != NULL && ctx->code == SNMP_RET_OK &&
856			    community != COMM_INITIALIZE)
857				lm_start(mdep->m);
858			free(mdep->path);
859		}
860		return (SNMP_ERR_NOERROR);
861	}
862	abort();
863}
864
865int
866op_modules(struct snmp_context *ctx, struct snmp_value *value,
867    u_int sub, u_int iidx, enum snmp_op op)
868{
869	asn_subid_t which = value->var.subs[sub - 1];
870	struct lmodule *m;
871	u_char *section, *ptr;
872	size_t seclen;
873	struct module_dep *mdep;
874	struct asn_oid idx;
875
876	switch (op) {
877
878	  case SNMP_OP_GETNEXT:
879		if ((m = NEXT_OBJECT_OID(&lmodules, &value->var, sub)) == NULL)
880			return (SNMP_ERR_NOSUCHNAME);
881		index_append(&value->var, sub, &m->index);
882		break;
883
884	  case SNMP_OP_GET:
885		if ((m = FIND_OBJECT_OID(&lmodules, &value->var, sub)) == NULL)
886			return (SNMP_ERR_NOSUCHNAME);
887		break;
888
889	  case SNMP_OP_SET:
890		m = FIND_OBJECT_OID(&lmodules, &value->var, sub);
891		if (which != LEAF_begemotSnmpdModulePath) {
892			if (m == NULL)
893				return (SNMP_ERR_NO_CREATION);
894			return (SNMP_ERR_NOT_WRITEABLE);
895		}
896
897		/* the errors in the next few statements can only happen when
898		 * m is NULL, hence the NO_CREATION error. */
899		if (index_decode(&value->var, sub, iidx,
900		    &section, &seclen))
901			return (SNMP_ERR_NO_CREATION);
902
903		/* check the section name */
904		if (seclen > LM_SECTION_MAX || seclen == 0) {
905			free(section);
906			return (SNMP_ERR_NO_CREATION);
907		}
908		for (ptr = section; ptr < section + seclen; ptr++)
909			if (!isascii(*ptr) || !isalnum(*ptr)) {
910				free(section);
911				return (SNMP_ERR_NO_CREATION);
912			}
913		if (!isalpha(section[0])) {
914			free(section);
915			return (SNMP_ERR_NO_CREATION);
916		}
917
918		/* check the path */
919		for (ptr = value->v.octetstring.octets;
920		     ptr < value->v.octetstring.octets + value->v.octetstring.len;
921		     ptr++) {
922			if (*ptr == '\0') {
923				free(section);
924				return (SNMP_ERR_WRONG_VALUE);
925			}
926		}
927
928		if (m == NULL) {
929			if (value->v.octetstring.len == 0) {
930				free(section);
931				return (SNMP_ERR_INCONS_VALUE);
932			}
933		} else {
934			if (value->v.octetstring.len != 0) {
935				free(section);
936				return (SNMP_ERR_INCONS_VALUE);
937			}
938		}
939
940		asn_slice_oid(&idx, &value->var, sub, value->var.len);
941
942		/* so far, so good */
943		mdep = (struct module_dep *)(void *)snmp_dep_lookup(ctx,
944		    &oid_begemotSnmpdModuleTable, &idx,
945		    sizeof(*mdep), dep_modules);
946		if (mdep == NULL) {
947			free(section);
948			return (SNMP_ERR_RES_UNAVAIL);
949		}
950
951		if (mdep->section[0] != '\0') {
952			/* two writes to the same entry - bad */
953			free(section);
954			return (SNMP_ERR_INCONS_VALUE);
955		}
956
957		strncpy(mdep->section, section, seclen);
958		mdep->section[seclen] = '\0';
959		free(section);
960
961		if (value->v.octetstring.len == 0)
962			mdep->path = NULL;
963		else {
964			if ((mdep->path = malloc(value->v.octetstring.len + 1)) == NULL)
965				return (SNMP_ERR_RES_UNAVAIL);
966			strncpy(mdep->path, value->v.octetstring.octets,
967			    value->v.octetstring.len);
968			mdep->path[value->v.octetstring.len] = '\0';
969		}
970		ctx->scratch->ptr1 = mdep;
971		return (SNMP_ERR_NOERROR);
972
973	  case SNMP_OP_ROLLBACK:
974	  case SNMP_OP_COMMIT:
975		return (SNMP_ERR_NOERROR);
976
977	  default:
978		abort();
979	}
980
981	switch (which) {
982
983	  case LEAF_begemotSnmpdModulePath:
984		return (string_get(value, m->path, -1));
985
986	  case LEAF_begemotSnmpdModuleComment:
987		return (string_get(value, m->config->comment, -1));
988	}
989	abort();
990}
991
992int
993op_snmp_set(struct snmp_context *ctx __unused, struct snmp_value *value,
994    u_int sub, u_int iidx __unused, enum snmp_op op)
995{
996	switch (op) {
997
998	  case SNMP_OP_GETNEXT:
999		abort();
1000
1001	  case SNMP_OP_GET:
1002		switch (value->var.subs[sub - 1]) {
1003
1004		  case LEAF_snmpSetSerialNo:
1005			value->v.integer = snmp_serial_no;
1006			break;
1007
1008		  default:
1009			abort();
1010		}
1011		return (SNMP_ERR_NOERROR);
1012
1013	  case SNMP_OP_SET:
1014		switch (value->var.subs[sub - 1]) {
1015
1016		  case LEAF_snmpSetSerialNo:
1017			if (value->v.integer != snmp_serial_no)
1018				return (SNMP_ERR_INCONS_VALUE);
1019			break;
1020
1021		  default:
1022			abort();
1023		}
1024		return (SNMP_ERR_NOERROR);
1025
1026	  case SNMP_OP_ROLLBACK:
1027		return (SNMP_ERR_NOERROR);
1028
1029	  case SNMP_OP_COMMIT:
1030		if (snmp_serial_no++ == 2147483647)
1031			snmp_serial_no = 0;
1032		return (SNMP_ERR_NOERROR);
1033	}
1034	abort();
1035}
1036
1037/*
1038 * SNMP Engine
1039 */
1040int
1041op_snmp_engine(struct snmp_context *ctx __unused, struct snmp_value *value,
1042    u_int sub, u_int iidx __unused, enum snmp_op op)
1043{
1044	asn_subid_t which = value->var.subs[sub - 1];
1045
1046	switch (op) {
1047	case SNMP_OP_GETNEXT:
1048		abort();
1049
1050	case SNMP_OP_GET:
1051		break;
1052
1053	case SNMP_OP_SET:
1054		if (community != COMM_INITIALIZE)
1055			return (SNMP_ERR_NOT_WRITEABLE);
1056		switch (which) {
1057		case LEAF_snmpEngineID:
1058			if (value->v.octetstring.len > SNMP_ENGINE_ID_SIZ)
1059				return (SNMP_ERR_WRONG_VALUE);
1060			ctx->scratch->ptr1 = malloc(snmpd_engine.engine_len);
1061			if (ctx->scratch->ptr1 == NULL)
1062				return (SNMP_ERR_GENERR);
1063			memcpy(ctx->scratch->ptr1, snmpd_engine.engine_id,
1064			    snmpd_engine.engine_len);
1065			ctx->scratch->int1 = snmpd_engine.engine_len;
1066			snmpd_engine.engine_len = value->v.octetstring.len;
1067			memcpy(snmpd_engine.engine_id,
1068			    value->v.octetstring.octets,
1069			    value->v.octetstring.len);
1070			break;
1071
1072		case LEAF_snmpEngineMaxMessageSize:
1073			ctx->scratch->int1 = snmpd_engine.max_msg_size;
1074			snmpd_engine.max_msg_size = value->v.integer;
1075			break;
1076
1077		default:
1078			return (SNMP_ERR_NOT_WRITEABLE);
1079		}
1080		return (SNMP_ERR_NOERROR);
1081
1082	case SNMP_OP_ROLLBACK:
1083		switch (which) {
1084		case LEAF_snmpEngineID:
1085			snmpd_engine.engine_len = ctx->scratch->int1;
1086			memcpy(snmpd_engine.engine_id, ctx->scratch->ptr1,
1087			    snmpd_engine.engine_len);
1088			free(ctx->scratch->ptr1);
1089			break;
1090
1091		case LEAF_snmpEngineMaxMessageSize:
1092			snmpd_engine.max_msg_size = ctx->scratch->int1;
1093			break;
1094
1095		default:
1096			abort();
1097		}
1098		return (SNMP_ERR_NOERROR);
1099
1100	case SNMP_OP_COMMIT:
1101		if (which == LEAF_snmpEngineID) {
1102			if (set_snmpd_engine() < 0) {
1103				snmpd_engine.engine_len = ctx->scratch->int1;
1104				memcpy(snmpd_engine.engine_id,
1105				    ctx->scratch->ptr1, ctx->scratch->int1);
1106			}
1107			free(ctx->scratch->ptr1);
1108		}
1109		return (SNMP_ERR_NOERROR);
1110	}
1111
1112
1113	switch (which) {
1114	case LEAF_snmpEngineID:
1115		return (string_get(value, snmpd_engine.engine_id,
1116		    snmpd_engine.engine_len));
1117	case LEAF_snmpEngineBoots:
1118		value->v.integer = snmpd_engine.engine_boots;
1119		break;
1120	case LEAF_snmpEngineTime:
1121		snmpd_engine.engine_time = (get_ticks() - start_tick) / 100ULL;
1122		value->v.integer = snmpd_engine.engine_time;
1123		break;
1124	case LEAF_snmpEngineMaxMessageSize:
1125		value->v.integer = snmpd_engine.max_msg_size;
1126		break;
1127	default:
1128		return (SNMP_ERR_NOSUCHNAME);
1129	}
1130
1131	return (SNMP_ERR_NOERROR);
1132}
1133
1134/*
1135 * Transport table
1136 */
1137int
1138op_transport_table(struct snmp_context *ctx __unused, struct snmp_value *value,
1139    u_int sub, u_int iidx, enum snmp_op op)
1140{
1141	asn_subid_t which = value->var.subs[sub - 1];
1142	struct transport *t;
1143	u_char *tname, *ptr;
1144	size_t tnamelen;
1145
1146	switch (op) {
1147
1148	  case SNMP_OP_GETNEXT:
1149		if ((t = NEXT_OBJECT_OID(&transport_list, &value->var, sub))
1150		    == NULL)
1151			return (SNMP_ERR_NOSUCHNAME);
1152		index_append(&value->var, sub, &t->index);
1153		break;
1154
1155	  case SNMP_OP_GET:
1156		if ((t = FIND_OBJECT_OID(&transport_list, &value->var, sub))
1157		    == NULL)
1158			return (SNMP_ERR_NOSUCHNAME);
1159		break;
1160
1161	  case SNMP_OP_SET:
1162		t = FIND_OBJECT_OID(&transport_list, &value->var, sub);
1163		if (which != LEAF_begemotSnmpdTransportStatus) {
1164			if (t == NULL)
1165				return (SNMP_ERR_NO_CREATION);
1166			return (SNMP_ERR_NOT_WRITEABLE);
1167		}
1168
1169		/* the errors in the next few statements can only happen when
1170		 * t is NULL, hence the NO_CREATION error. */
1171		if (index_decode(&value->var, sub, iidx,
1172		    &tname, &tnamelen))
1173			return (SNMP_ERR_NO_CREATION);
1174
1175		/* check the section name */
1176		if (tnamelen >= TRANS_NAMELEN || tnamelen == 0) {
1177			free(tname);
1178			return (SNMP_ERR_NO_CREATION);
1179		}
1180		for (ptr = tname; ptr < tname + tnamelen; ptr++) {
1181			if (!isascii(*ptr) || !isalnum(*ptr)) {
1182				free(tname);
1183				return (SNMP_ERR_NO_CREATION);
1184			}
1185		}
1186
1187		/* for now */
1188		return (SNMP_ERR_NOT_WRITEABLE);
1189
1190	  case SNMP_OP_ROLLBACK:
1191	  case SNMP_OP_COMMIT:
1192		return (SNMP_ERR_NOERROR);
1193	  default:
1194		abort();
1195	}
1196
1197	switch (which) {
1198
1199	    case LEAF_begemotSnmpdTransportStatus:
1200		value->v.integer = 1;
1201		break;
1202
1203	    case LEAF_begemotSnmpdTransportOid:
1204		memcpy(&value->v.oid, &t->vtab->id, sizeof(t->vtab->id));
1205		break;
1206	}
1207	return (SNMP_ERR_NOERROR);
1208}
1209