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
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 a string value from the KERN sysctl subtree.
64 */
65static char *
66act_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 */
88static char *
89act_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 */
111int
112init_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 */
170int
171init_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
204int
205set_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
236save_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 */
249int
250op_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 */
362int
363op_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 */
454int
455op_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 */
511int
512op_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 */
594int
595op_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 */
636int
637op_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 */
757int
758op_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 */
827struct 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
834static int
835dep_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
888int
889op_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
1015int
1016op_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 */
1063int
1064op_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 */
1160int
1161op_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