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