xref: /freebsd/contrib/bsnmp/snmpd/action.c (revision 1f4bcc459a76b7aa664f3fd557684cd0ba6da352)
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 an integer value from the KERN sysctl subtree.
64  */
65 static char *
66 act_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  */
88 int
89 init_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  */
147 int
148 init_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 
181 int
182 set_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 
213 save_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  */
226 int
227 op_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  */
339 int
340 op_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  */
431 int
432 op_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  */
488 int
489 op_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  */
571 int
572 op_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  */
613 int
614 op_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  */
734 int
735 op_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  */
804 struct 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 
811 static int
812 dep_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 
865 int
866 op_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 
992 int
993 op_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  */
1040 int
1041 op_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  */
1137 int
1138 op_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