pf_snmp.c revision 143611
1/*-
2 * Copyright (c) 2005 Philip Paeps <philip@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c 143611 2005-03-14 22:16:39Z philip $
27 */
28
29#include <bsnmp/snmpmod.h>
30
31#include <net/pfvar.h>
32#include <sys/ioctl.h>
33
34#include <errno.h>
35#include <fcntl.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <syslog.h>
40#include <unistd.h>
41
42#include "pf_oid.h"
43#include "pf_tree.h"
44
45struct lmodule *module;
46
47static int dev = -1;
48static int started;
49static uint32_t pf_tick;
50
51static struct pf_status pfs;
52
53enum { IN, OUT };
54enum { IPV4, IPV6 };
55enum { PASS, BLOCK };
56
57#define PFI_IFTYPE_GROUP	0
58#define PFI_IFTYPE_INSTANCE	1
59#define PFI_IFTYPE_DETACHED	2
60
61struct pfi_entry {
62	struct pfi_if	pfi;
63	u_int		index;
64	TAILQ_ENTRY(pfi_entry) link;
65};
66TAILQ_HEAD(pfi_table, pfi_entry);
67
68static struct pfi_table pfi_table;
69static time_t pfi_table_age;
70static int pfi_table_count;
71
72#define PFI_TABLE_MAXAGE	5
73
74struct pft_entry {
75	struct pfr_tstats pft;
76	u_int		index;
77	TAILQ_ENTRY(pft_entry) link;
78};
79TAILQ_HEAD(pft_table, pft_entry);
80
81static struct pft_table pft_table;
82static time_t pft_table_age;
83static int pft_table_count;
84
85#define PFT_TABLE_MAXAGE	5
86
87struct pfq_entry {
88	struct pf_altq	altq;
89	u_int		index;
90	TAILQ_ENTRY(pfq_entry) link;
91};
92TAILQ_HEAD(pfq_table, pfq_entry);
93
94static struct pfq_table pfq_table;
95static time_t pfq_table_age;
96static int pfq_table_count;
97
98#define PFQ_TABLE_MAXAGE	5
99
100/* Forward declarations */
101static int pfi_refresh(void);
102static int pfq_refresh(void);
103static int pfs_refresh(void);
104static int pft_refresh(void);
105static struct pfi_entry * pfi_table_find(u_int idx);
106static struct pfq_entry * pfq_table_find(u_int idx);
107static struct pft_entry * pft_table_find(u_int idx);
108
109int
110pf_status(struct snmp_context __unused *ctx, struct snmp_value *val,
111	u_int sub, u_int __unused vindex, enum snmp_op op)
112{
113	asn_subid_t	which = val->var.subs[sub - 1];
114	time_t		runtime;
115	unsigned char	str[128];
116
117	if (op == SNMP_OP_SET)
118		return (SNMP_ERR_NOT_WRITEABLE);
119
120	if (op == SNMP_OP_GET) {
121		if (pfs_refresh() == -1)
122			return (SNMP_ERR_GENERR);
123
124		switch (which) {
125			case LEAF_pfStatusRunning:
126			    val->v.uint32 = pfs.running;
127			    break;
128			case LEAF_pfStatusRuntime:
129			    runtime = (pfs.since > 0) ?
130				time(NULL) - pfs.since : 0;
131			    val->v.uint32 = runtime * 100;
132			    break;
133			case LEAF_pfStatusDebug:
134			    val->v.uint32 = pfs.debug;
135			    break;
136			case LEAF_pfStatusHostId:
137			    sprintf(str, "0x%08x", ntohl(pfs.hostid));
138			    return (string_get(val, str, strlen(str)));
139
140			default:
141			    return (SNMP_ERR_NOSUCHNAME);
142		}
143
144		return (SNMP_ERR_NOERROR);
145	}
146
147	abort();
148}
149
150int
151pf_counter(struct snmp_context __unused *ctx, struct snmp_value *val,
152	u_int sub, u_int __unused vindex, enum snmp_op op)
153{
154	asn_subid_t	which = val->var.subs[sub - 1];
155
156	if (op == SNMP_OP_SET)
157		return (SNMP_ERR_NOT_WRITEABLE);
158
159	if (op == SNMP_OP_GET) {
160		if (pfs_refresh() == -1)
161			return (SNMP_ERR_GENERR);
162
163		switch (which) {
164			case LEAF_pfCounterMatch:
165				val->v.counter64 = pfs.counters[PFRES_MATCH];
166				break;
167			case LEAF_pfCounterBadOffset:
168				val->v.counter64 = pfs.counters[PFRES_BADOFF];
169				break;
170			case LEAF_pfCounterFragment:
171				val->v.counter64 = pfs.counters[PFRES_FRAG];
172				break;
173			case LEAF_pfCounterShort:
174				val->v.counter64 = pfs.counters[PFRES_SHORT];
175				break;
176			case LEAF_pfCounterNormalize:
177				val->v.counter64 = pfs.counters[PFRES_NORM];
178				break;
179			case LEAF_pfCounterMemDrop:
180				val->v.counter64 = pfs.counters[PFRES_MEMORY];
181				break;
182
183			default:
184				return (SNMP_ERR_NOSUCHNAME);
185		}
186
187		return (SNMP_ERR_NOERROR);
188	}
189
190	abort();
191}
192
193int
194pf_statetable(struct snmp_context __unused *ctx, struct snmp_value *val,
195	u_int sub, u_int __unused vindex, enum snmp_op op)
196{
197	asn_subid_t	which = val->var.subs[sub - 1];
198
199	if (op == SNMP_OP_SET)
200		return (SNMP_ERR_NOT_WRITEABLE);
201
202	if (op == SNMP_OP_GET) {
203		if (pfs_refresh() == -1)
204			return (SNMP_ERR_GENERR);
205
206		switch (which) {
207			case LEAF_pfStateTableCount:
208				val->v.uint32 = pfs.states;
209				break;
210			case LEAF_pfStateTableSearches:
211				val->v.counter64 =
212				    pfs.fcounters[FCNT_STATE_SEARCH];
213				break;
214			case LEAF_pfStateTableInserts:
215				val->v.counter64 =
216				    pfs.fcounters[FCNT_STATE_INSERT];
217				break;
218			case LEAF_pfStateTableRemovals:
219				val->v.counter64 =
220				    pfs.fcounters[FCNT_STATE_REMOVALS];
221				break;
222
223			default:
224				return (SNMP_ERR_NOSUCHNAME);
225		}
226
227		return (SNMP_ERR_NOERROR);
228	}
229
230	abort();
231}
232
233int
234pf_srcnodes(struct snmp_context __unused *ctx, struct snmp_value *val,
235	u_int sub, u_int __unused vindex, enum snmp_op op)
236{
237	asn_subid_t	which = val->var.subs[sub - 1];
238
239	if (op == SNMP_OP_SET)
240		return (SNMP_ERR_NOT_WRITEABLE);
241
242	if (op == SNMP_OP_GET) {
243		if (pfs_refresh() == -1)
244			return (SNMP_ERR_GENERR);
245
246		switch (which) {
247			case LEAF_pfSrcNodesCount:
248				val->v.uint32 = pfs.src_nodes;
249				break;
250			case LEAF_pfSrcNodesSearches:
251				val->v.counter64 =
252				    pfs.scounters[SCNT_SRC_NODE_SEARCH];
253				break;
254			case LEAF_pfSrcNodesInserts:
255				val->v.counter64 =
256				    pfs.scounters[SCNT_SRC_NODE_INSERT];
257				break;
258			case LEAF_pfSrcNodesRemovals:
259				val->v.counter64 =
260				    pfs.scounters[SCNT_SRC_NODE_REMOVALS];
261				break;
262
263			default:
264				return (SNMP_ERR_NOSUCHNAME);
265		}
266
267		return (SNMP_ERR_NOERROR);
268	}
269
270	abort();
271}
272
273int
274pf_limits(struct snmp_context __unused *ctx, struct snmp_value *val,
275	u_int sub, u_int __unused vindex, enum snmp_op op)
276{
277	asn_subid_t		which = val->var.subs[sub - 1];
278	struct pfioc_limit	pl;
279
280	if (op == SNMP_OP_SET)
281		return (SNMP_ERR_NOT_WRITEABLE);
282
283	if (op == SNMP_OP_GET) {
284		bzero(&pl, sizeof(struct pfioc_limit));
285
286		switch (which) {
287			case LEAF_pfLimitsStates:
288				pl.index = PF_LIMIT_STATES;
289				break;
290			case LEAF_pfLimitsSrcNodes:
291				pl.index = PF_LIMIT_SRC_NODES;
292				break;
293			case LEAF_pfLimitsFrags:
294				pl.index = PF_LIMIT_FRAGS;
295				break;
296
297			default:
298				return (SNMP_ERR_NOSUCHNAME);
299		}
300
301		if (ioctl(dev, DIOCGETLIMIT, &pl)) {
302			syslog(LOG_ERR, "pf_limits(): ioctl(): %s",
303			    strerror(errno));
304			return (SNMP_ERR_GENERR);
305		}
306
307		val->v.uint32 = pl.limit;
308
309		return (SNMP_ERR_NOERROR);
310	}
311
312	abort();
313}
314
315int
316pf_timeouts(struct snmp_context __unused *ctx, struct snmp_value *val,
317	u_int sub, u_int __unused vindex, enum snmp_op op)
318{
319	asn_subid_t	which = val->var.subs[sub - 1];
320	struct pfioc_tm	pt;
321
322	if (op == SNMP_OP_SET)
323		return (SNMP_ERR_NOT_WRITEABLE);
324
325	if (op == SNMP_OP_GET) {
326		bzero(&pt, sizeof(struct pfioc_tm));
327
328		switch (which) {
329			case LEAF_pfTimeoutsTcpFirst:
330				pt.timeout = PFTM_TCP_FIRST_PACKET;
331				break;
332			case LEAF_pfTimeoutsTcpOpening:
333				pt.timeout = PFTM_TCP_OPENING;
334				break;
335			case LEAF_pfTimeoutsTcpEstablished:
336				pt.timeout = PFTM_TCP_ESTABLISHED;
337				break;
338			case LEAF_pfTimeoutsTcpClosing:
339				pt.timeout = PFTM_TCP_CLOSING;
340				break;
341			case LEAF_pfTimeoutsTcpFinWait:
342				pt.timeout = PFTM_TCP_FIN_WAIT;
343				break;
344			case LEAF_pfTimeoutsTcpClosed:
345				pt.timeout = PFTM_TCP_CLOSED;
346				break;
347			case LEAF_pfTimeoutsUdpFirst:
348				pt.timeout = PFTM_UDP_FIRST_PACKET;
349				break;
350			case LEAF_pfTimeoutsUdpSingle:
351				pt.timeout = PFTM_UDP_SINGLE;
352				break;
353			case LEAF_pfTimeoutsUdpMultiple:
354				pt.timeout = PFTM_UDP_MULTIPLE;
355				break;
356			case LEAF_pfTimeoutsIcmpFirst:
357				pt.timeout = PFTM_ICMP_FIRST_PACKET;
358				break;
359			case LEAF_pfTimeoutsIcmpError:
360				pt.timeout = PFTM_ICMP_ERROR_REPLY;
361				break;
362			case LEAF_pfTimeoutsOtherFirst:
363				pt.timeout = PFTM_OTHER_FIRST_PACKET;
364				break;
365			case LEAF_pfTimeoutsOtherSingle:
366				pt.timeout = PFTM_OTHER_SINGLE;
367				break;
368			case LEAF_pfTimeoutsOtherMultiple:
369				pt.timeout = PFTM_OTHER_MULTIPLE;
370				break;
371			case LEAF_pfTimeoutsFragment:
372				pt.timeout = PFTM_FRAG;
373				break;
374			case LEAF_pfTimeoutsInterval:
375				pt.timeout = PFTM_INTERVAL;
376				break;
377			case LEAF_pfTimeoutsAdaptiveStart:
378				pt.timeout = PFTM_ADAPTIVE_START;
379				break;
380			case LEAF_pfTimeoutsAdaptiveEnd:
381				pt.timeout = PFTM_ADAPTIVE_END;
382				break;
383			case LEAF_pfTimeoutsSrcNode:
384				pt.timeout = PFTM_SRC_NODE;
385				break;
386
387			default:
388				return (SNMP_ERR_NOSUCHNAME);
389		}
390
391		if (ioctl(dev, DIOCGETTIMEOUT, &pt)) {
392			syslog(LOG_ERR, "pf_timeouts(): ioctl(): %s",
393			    strerror(errno));
394			return (SNMP_ERR_GENERR);
395		}
396
397		val->v.integer = pt.seconds;
398
399		return (SNMP_ERR_NOERROR);
400	}
401
402	abort();
403}
404
405int
406pf_logif(struct snmp_context __unused *ctx, struct snmp_value *val,
407	u_int sub, u_int __unused vindex, enum snmp_op op)
408{
409	asn_subid_t	which = val->var.subs[sub - 1];
410	unsigned char	str[IFNAMSIZ];
411
412	if (op == SNMP_OP_SET)
413		return (SNMP_ERR_NOT_WRITEABLE);
414
415	if (op == SNMP_OP_GET) {
416		if (pfs_refresh() == -1)
417			return (SNMP_ERR_GENERR);
418
419		switch (which) {
420	 		case LEAF_pfLogInterfaceName:
421				strlcpy(str, pfs.ifname, sizeof str);
422				return (string_get(val, str, strlen(str)));
423			case LEAF_pfLogInterfaceIp4BytesIn:
424				val->v.counter64 = pfs.bcounters[IPV4][IN];
425				break;
426			case LEAF_pfLogInterfaceIp4BytesOut:
427				val->v.counter64 = pfs.bcounters[IPV4][OUT];
428				break;
429			case LEAF_pfLogInterfaceIp4PktsInPass:
430				val->v.counter64 =
431				    pfs.pcounters[IPV4][IN][PF_PASS];
432				break;
433			case LEAF_pfLogInterfaceIp4PktsInDrop:
434				val->v.counter64 =
435				    pfs.pcounters[IPV4][IN][PF_DROP];
436				break;
437			case LEAF_pfLogInterfaceIp4PktsOutPass:
438				val->v.counter64 =
439				    pfs.pcounters[IPV4][OUT][PF_PASS];
440				break;
441			case LEAF_pfLogInterfaceIp4PktsOutDrop:
442				val->v.counter64 =
443				    pfs.pcounters[IPV4][OUT][PF_DROP];
444				break;
445			case LEAF_pfLogInterfaceIp6BytesIn:
446				val->v.counter64 = pfs.bcounters[IPV6][IN];
447				break;
448			case LEAF_pfLogInterfaceIp6BytesOut:
449				val->v.counter64 = pfs.bcounters[IPV6][OUT];
450				break;
451			case LEAF_pfLogInterfaceIp6PktsInPass:
452				val->v.counter64 =
453				    pfs.pcounters[IPV6][IN][PF_PASS];
454				break;
455			case LEAF_pfLogInterfaceIp6PktsInDrop:
456				val->v.counter64 =
457				    pfs.pcounters[IPV6][IN][PF_DROP];
458				break;
459			case LEAF_pfLogInterfaceIp6PktsOutPass:
460				val->v.counter64 =
461				    pfs.pcounters[IPV6][OUT][PF_PASS];
462				break;
463			case LEAF_pfLogInterfaceIp6PktsOutDrop:
464				val->v.counter64 =
465				    pfs.pcounters[IPV6][OUT][PF_DROP];
466				break;
467
468			default:
469				return (SNMP_ERR_NOSUCHNAME);
470		}
471
472		return (SNMP_ERR_NOERROR);
473	}
474
475	abort();
476}
477
478int
479pf_interfaces(struct snmp_context __unused *ctx, struct snmp_value *val,
480	u_int sub, u_int __unused vindex, enum snmp_op op)
481{
482	asn_subid_t	which = val->var.subs[sub - 1];
483
484	if (op == SNMP_OP_SET)
485		return (SNMP_ERR_NOT_WRITEABLE);
486
487	if (op == SNMP_OP_GET) {
488		if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
489			if (pfi_refresh() == -1)
490			    return (SNMP_ERR_GENERR);
491
492		switch (which) {
493			case LEAF_pfInterfacesIfNumber:
494				val->v.uint32 = pfi_table_count;
495				break;
496
497			default:
498				return (SNMP_ERR_NOSUCHNAME);
499		}
500
501		return (SNMP_ERR_NOERROR);
502	}
503
504	abort();
505}
506
507int
508pf_iftable(struct snmp_context __unused *ctx, struct snmp_value *val,
509	u_int sub, u_int __unused vindex, enum snmp_op op)
510{
511	asn_subid_t	which = val->var.subs[sub - 1];
512	struct pfi_entry *e = NULL;
513
514	switch (op) {
515		case SNMP_OP_SET:
516			return (SNMP_ERR_NOT_WRITEABLE);
517		case SNMP_OP_GETNEXT:
518			if ((e = NEXT_OBJECT_INT(&pfi_table,
519			    &val->var, sub)) == NULL)
520				return (SNMP_ERR_NOSUCHNAME);
521			val->var.len = sub + 1;
522			val->var.subs[sub] = e->index;
523			break;
524		case SNMP_OP_GET:
525			if (val->var.len - sub != 1)
526				return (SNMP_ERR_NOSUCHNAME);
527			if ((e = pfi_table_find(val->var.subs[sub])) == NULL)
528				return (SNMP_ERR_NOSUCHNAME);
529			break;
530
531		case SNMP_OP_COMMIT:
532		case SNMP_OP_ROLLBACK:
533		default:
534			abort();
535	}
536
537	if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
538		pfi_refresh();
539
540	switch (which) {
541		case LEAF_pfInterfacesIfDescr:
542			return (string_get(val, e->pfi.pfif_name, -1));
543		case LEAF_pfInterfacesIfType:
544			val->v.integer = PFI_IFTYPE_INSTANCE;
545			break;
546		case LEAF_pfInterfacesIfTZero:
547			val->v.uint32 =
548			    (time(NULL) - e->pfi.pfif_tzero) * 100;
549			break;
550		case LEAF_pfInterfacesIfRefsState:
551			val->v.uint32 = e->pfi.pfif_states;
552			break;
553		case LEAF_pfInterfacesIfRefsRule:
554			val->v.uint32 = e->pfi.pfif_rules;
555			break;
556		case LEAF_pfInterfacesIf4BytesInPass:
557			val->v.counter64 =
558			    e->pfi.pfif_bytes[IPV4][IN][PASS];
559			break;
560		case LEAF_pfInterfacesIf4BytesInBlock:
561			val->v.counter64 =
562			    e->pfi.pfif_bytes[IPV4][IN][BLOCK];
563			break;
564		case LEAF_pfInterfacesIf4BytesOutPass:
565			val->v.counter64 =
566			    e->pfi.pfif_bytes[IPV4][OUT][PASS];
567			break;
568		case LEAF_pfInterfacesIf4BytesOutBlock:
569			val->v.counter64 =
570			    e->pfi.pfif_bytes[IPV4][OUT][BLOCK];
571			break;
572		case LEAF_pfInterfacesIf4PktsInPass:
573			val->v.counter64 =
574			    e->pfi.pfif_packets[IPV4][IN][PASS];
575			break;
576		case LEAF_pfInterfacesIf4PktsInBlock:
577			val->v.counter64 =
578			    e->pfi.pfif_packets[IPV4][IN][BLOCK];
579			break;
580		case LEAF_pfInterfacesIf4PktsOutPass:
581			val->v.counter64 =
582			    e->pfi.pfif_packets[IPV4][OUT][PASS];
583			break;
584		case LEAF_pfInterfacesIf4PktsOutBlock:
585			val->v.counter64 =
586			    e->pfi.pfif_packets[IPV4][OUT][BLOCK];
587			break;
588		case LEAF_pfInterfacesIf6BytesInPass:
589			val->v.counter64 =
590			    e->pfi.pfif_bytes[IPV6][IN][PASS];
591			break;
592		case LEAF_pfInterfacesIf6BytesInBlock:
593			val->v.counter64 =
594			    e->pfi.pfif_bytes[IPV6][IN][BLOCK];
595			break;
596		case LEAF_pfInterfacesIf6BytesOutPass:
597			val->v.counter64 =
598			    e->pfi.pfif_bytes[IPV6][OUT][PASS];
599			break;
600		case LEAF_pfInterfacesIf6BytesOutBlock:
601			val->v.counter64 =
602			    e->pfi.pfif_bytes[IPV6][OUT][BLOCK];
603			break;
604		case LEAF_pfInterfacesIf6PktsInPass:
605			val->v.counter64 =
606			    e->pfi.pfif_packets[IPV6][IN][PASS];
607			break;
608		case LEAF_pfInterfacesIf6PktsInBlock:
609			val->v.counter64 =
610			    e->pfi.pfif_packets[IPV6][IN][BLOCK];
611			break;
612		case LEAF_pfInterfacesIf6PktsOutPass:
613			val->v.counter64 =
614			    e->pfi.pfif_packets[IPV6][OUT][PASS];
615			break;
616		case LEAF_pfInterfacesIf6PktsOutBlock:
617			val->v.counter64 =
618			    e->pfi.pfif_packets[IPV6][OUT][BLOCK];
619			break;
620
621		default:
622			return (SNMP_ERR_NOSUCHNAME);
623	}
624
625	return (SNMP_ERR_NOERROR);
626}
627
628int
629pf_tables(struct snmp_context __unused *ctx, struct snmp_value *val,
630	u_int sub, u_int __unused vindex, enum snmp_op op)
631{
632	asn_subid_t	which = val->var.subs[sub - 1];
633
634	if (op == SNMP_OP_SET)
635		return (SNMP_ERR_NOT_WRITEABLE);
636
637	if (op == SNMP_OP_GET) {
638		if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
639			if (pft_refresh() == -1)
640			    return (SNMP_ERR_GENERR);
641
642		switch (which) {
643			case LEAF_pfTablesTblNumber:
644				val->v.uint32 = pft_table_count;
645				break;
646
647			default:
648				return (SNMP_ERR_NOSUCHNAME);
649		}
650
651		return (SNMP_ERR_NOERROR);
652	}
653
654	abort();
655}
656
657int
658pf_tbltable(struct snmp_context __unused *ctx, struct snmp_value *val,
659	u_int sub, u_int __unused vindex, enum snmp_op op)
660{
661	asn_subid_t	which = val->var.subs[sub - 1];
662	struct pft_entry *e = NULL;
663
664	switch (op) {
665		case SNMP_OP_SET:
666			return (SNMP_ERR_NOT_WRITEABLE);
667		case SNMP_OP_GETNEXT:
668			if ((e = NEXT_OBJECT_INT(&pft_table,
669			    &val->var, sub)) == NULL)
670				return (SNMP_ERR_NOSUCHNAME);
671			val->var.len = sub + 1;
672			val->var.subs[sub] = e->index;
673			break;
674		case SNMP_OP_GET:
675			if (val->var.len - sub != 1)
676				return (SNMP_ERR_NOSUCHNAME);
677			if ((e = pft_table_find(val->var.subs[sub])) == NULL)
678				return (SNMP_ERR_NOSUCHNAME);
679			break;
680
681		case SNMP_OP_COMMIT:
682		case SNMP_OP_ROLLBACK:
683		default:
684			abort();
685	}
686
687	if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
688		pft_refresh();
689
690	switch (which) {
691		case LEAF_pfTablesTblDescr:
692			return (string_get(val, e->pft.pfrts_name, -1));
693		case LEAF_pfTablesTblCount:
694			val->v.integer = e->pft.pfrts_cnt;
695			break;
696		case LEAF_pfTablesTblTZero:
697			val->v.uint32 =
698			    (time(NULL) - e->pft.pfrts_tzero) * 100;
699			break;
700		case LEAF_pfTablesTblRefsAnchor:
701			val->v.integer =
702			    e->pft.pfrts_refcnt[PFR_REFCNT_ANCHOR];
703			break;
704		case LEAF_pfTablesTblRefsRule:
705			val->v.integer =
706			    e->pft.pfrts_refcnt[PFR_REFCNT_RULE];
707			break;
708		case LEAF_pfTablesTblEvalMatch:
709			val->v.counter64 = e->pft.pfrts_match;
710			break;
711		case LEAF_pfTablesTblEvalNoMatch:
712			val->v.counter64 = e->pft.pfrts_nomatch;
713			break;
714		case LEAF_pfTablesTblBytesInPass:
715			val->v.counter64 =
716			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_PASS];
717			break;
718		case LEAF_pfTablesTblBytesInBlock:
719			val->v.counter64 =
720			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_BLOCK];
721			break;
722		case LEAF_pfTablesTblBytesInXPass:
723			val->v.counter64 =
724			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_XPASS];
725			break;
726		case LEAF_pfTablesTblBytesOutPass:
727			val->v.counter64 =
728			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_PASS];
729			break;
730		case LEAF_pfTablesTblBytesOutBlock:
731			val->v.counter64 =
732			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_BLOCK];
733			break;
734		case LEAF_pfTablesTblBytesOutXPass:
735			val->v.counter64 =
736			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_XPASS];
737			break;
738		case LEAF_pfTablesTblPktsInPass:
739			val->v.counter64 =
740			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_PASS];
741			break;
742		case LEAF_pfTablesTblPktsInBlock:
743			val->v.counter64 =
744			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_BLOCK];
745			break;
746		case LEAF_pfTablesTblPktsInXPass:
747			val->v.counter64 =
748			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_XPASS];
749			break;
750		case LEAF_pfTablesTblPktsOutPass:
751			val->v.counter64 =
752			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_PASS];
753			break;
754		case LEAF_pfTablesTblPktsOutBlock:
755			val->v.counter64 =
756			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_BLOCK];
757			break;
758		case LEAF_pfTablesTblPktsOutXPass:
759			val->v.counter64 =
760			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_XPASS];
761			break;
762
763		default:
764			return (SNMP_ERR_NOSUCHNAME);
765	}
766
767	return (SNMP_ERR_NOERROR);
768}
769
770int
771pf_tbladdr(struct snmp_context __unused *ctx, struct snmp_value __unused *val,
772	u_int __unused sub, u_int __unused vindex, enum snmp_op __unused op)
773{
774	return (SNMP_ERR_GENERR);
775}
776
777int
778pf_altq(struct snmp_context __unused *ctx, struct snmp_value *val,
779	u_int sub, u_int __unused vindex, enum snmp_op op)
780{
781	asn_subid_t	which = val->var.subs[sub - 1];
782
783	if (op == SNMP_OP_SET)
784		return (SNMP_ERR_NOT_WRITEABLE);
785
786	if (op == SNMP_OP_GET) {
787		if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
788			if (pfq_refresh() == -1)
789			    return (SNMP_ERR_GENERR);
790
791		switch (which) {
792			case LEAF_pfAltqQueueNumber:
793				val->v.uint32 = pfq_table_count;
794				break;
795
796			default:
797				return (SNMP_ERR_NOSUCHNAME);
798		}
799
800		return (SNMP_ERR_NOERROR);
801	}
802
803	abort();
804	return (SNMP_ERR_GENERR);
805}
806
807int
808pf_altqq(struct snmp_context __unused *ctx, struct snmp_value *val,
809	u_int sub, u_int __unused vindex, enum snmp_op op)
810{
811	asn_subid_t	which = val->var.subs[sub - 1];
812	struct pfq_entry *e = NULL;
813
814	switch (op) {
815		case SNMP_OP_SET:
816			return (SNMP_ERR_NOT_WRITEABLE);
817		case SNMP_OP_GETNEXT:
818			if ((e = NEXT_OBJECT_INT(&pfq_table,
819			    &val->var, sub)) == NULL)
820				return (SNMP_ERR_NOSUCHNAME);
821			val->var.len = sub + 1;
822			val->var.subs[sub] = e->index;
823			break;
824		case SNMP_OP_GET:
825			if (val->var.len - sub != 1)
826				return (SNMP_ERR_NOSUCHNAME);
827			if ((e = pfq_table_find(val->var.subs[sub])) == NULL)
828				return (SNMP_ERR_NOSUCHNAME);
829			break;
830
831		case SNMP_OP_COMMIT:
832		case SNMP_OP_ROLLBACK:
833		default:
834			abort();
835	}
836
837	if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
838		pfq_refresh();
839
840	switch (which) {
841		case LEAF_pfAltqQueueDescr:
842			return (string_get(val, e->altq.qname, -1));
843		case LEAF_pfAltqQueueParent:
844			return (string_get(val, e->altq.parent, -1));
845		case LEAF_pfAltqQueueScheduler:
846			val->v.integer = e->altq.scheduler;
847			break;
848		case LEAF_pfAltqQueueBandwidth:
849			val->v.uint32 = e->altq.bandwidth;
850			break;
851		case LEAF_pfAltqQueuePriority:
852			val->v.integer = e->altq.priority;
853			break;
854		case LEAF_pfAltqQueueLimit:
855			val->v.integer = e->altq.qlimit;
856			break;
857
858		default:
859			return (SNMP_ERR_NOSUCHNAME);
860	}
861
862	return (SNMP_ERR_NOERROR);
863}
864
865static struct pfi_entry *
866pfi_table_find(u_int idx)
867{
868	struct pfi_entry *e;
869
870	TAILQ_FOREACH(e, &pfi_table, link)
871		if (e->index == idx)
872			return (e);
873	return (NULL);
874}
875
876static struct pfq_entry *
877pfq_table_find(u_int idx)
878{
879	struct pfq_entry *e;
880	TAILQ_FOREACH(e, &pfq_table, link)
881		if (e->index == idx)
882			return (e);
883	return (NULL);
884}
885
886static struct pft_entry *
887pft_table_find(u_int idx)
888{
889	struct pft_entry *e;
890
891	TAILQ_FOREACH(e, &pft_table, link)
892		if (e->index == idx)
893			return (e);
894	return (NULL);
895}
896
897static int
898pfi_refresh(void)
899{
900	struct pfioc_iface io;
901	struct pfi_if *p;
902	struct pfi_entry *e;
903	int i, numifs = 1;
904
905	if (started && this_tick <= pf_tick)
906		return (0);
907
908	while (!TAILQ_EMPTY(&pfi_table)) {
909		e = TAILQ_FIRST(&pfi_table);
910		TAILQ_REMOVE(&pfi_table, e, link);
911		free(e);
912	}
913
914	bzero(&io, sizeof(io));
915	p = malloc(sizeof(struct pfi_if));
916	io.pfiio_flags = PFI_FLAG_INSTANCE;
917	io.pfiio_esize = sizeof(struct pfi_if);
918
919	for (;;) {
920		p = realloc(p, numifs * sizeof(struct pfi_if));
921		io.pfiio_size = numifs;
922		io.pfiio_buffer = p;
923
924		if (ioctl(dev, DIOCIGETIFACES, &io)) {
925			syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s",
926			    strerror(errno));
927			return (-1);
928		}
929
930		if (numifs >= io.pfiio_size)
931			break;
932
933		numifs = io.pfiio_size;
934	}
935
936	for (i = 0; i < numifs; i++) {
937		e = malloc(sizeof(struct pfi_entry));
938		e->index = i + 1;
939		memcpy(&e->pfi, p+i, sizeof(struct pfi_if));
940		TAILQ_INSERT_TAIL(&pfi_table, e, link);
941	}
942
943	pfi_table_age = time(NULL);
944	pfi_table_count = numifs;
945	pf_tick = this_tick;
946
947	free(p);
948	return (0);
949}
950
951static int
952pfq_refresh(void)
953{
954	struct pfioc_altq pa;
955	struct pfq_entry *e;
956	int i, numqs, ticket;
957
958	if (started && this_tick <= pf_tick)
959		return (0);
960
961	while (!TAILQ_EMPTY(&pfq_table)) {
962		e = TAILQ_FIRST(&pfq_table);
963		TAILQ_REMOVE(&pfq_table, e, link);
964		free(e);
965	}
966
967	bzero(&pa, sizeof(pa));
968
969	if (ioctl(dev, DIOCGETALTQS, &pa)) {
970		syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s",
971		    strerror(errno));
972		return (-1);
973	}
974
975	numqs = pa.nr;
976	ticket = pa.ticket;
977
978	for (i = 0; i < numqs; i++) {
979		e = malloc(sizeof(struct pfq_entry));
980		pa.ticket = ticket;
981		pa.nr = i;
982
983		if (ioctl(dev, DIOCGETALTQ, &pa)) {
984			syslog(LOG_ERR, "pfq_refresh(): "
985			    "ioctl(DIOCGETALTQ): %s",
986			    strerror(errno));
987			return (-1);
988		}
989
990		if (pa.altq.qid > 0) {
991			memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq));
992			e->index = pa.altq.qid;
993			pfq_table_count = i;
994			TAILQ_INSERT_TAIL(&pfq_table, e, link);
995		}
996	}
997
998	pfq_table_age = time(NULL);
999	pf_tick = this_tick;
1000
1001	return (0);
1002}
1003
1004static int
1005pfs_refresh(void)
1006{
1007	if (started && this_tick <= pf_tick)
1008		return (0);
1009
1010	bzero(&pfs, sizeof(struct pf_status));
1011
1012	if (ioctl(dev, DIOCGETSTATUS, &pfs)) {
1013		syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s",
1014		    strerror(errno));
1015		return (-1);
1016	}
1017
1018	pf_tick = this_tick;
1019	return (0);
1020}
1021
1022static int
1023pft_refresh(void)
1024{
1025	struct pfioc_table io;
1026	struct pfr_tstats *t;
1027	struct pft_entry *e;
1028	int i, numtbls = 1;
1029
1030	if (started && this_tick <= pf_tick)
1031		return (0);
1032
1033	while (!TAILQ_EMPTY(&pft_table)) {
1034		e = TAILQ_FIRST(&pft_table);
1035		TAILQ_REMOVE(&pft_table, e, link);
1036		free(e);
1037	}
1038
1039	bzero(&io, sizeof(io));
1040	t = malloc(sizeof(struct pfr_tstats));
1041	io.pfrio_esize = sizeof(struct pfr_tstats);
1042
1043	for (;;) {
1044		t = realloc(t, numtbls * sizeof(struct pfr_tstats));
1045		io.pfrio_size = numtbls;
1046		io.pfrio_buffer = t;
1047
1048		if (ioctl(dev, DIOCRGETTSTATS, &io)) {
1049			syslog(LOG_ERR, "pft_refresh(): ioctl(): %s",
1050			    strerror(errno));
1051			return (-1);
1052		}
1053
1054		if (numtbls >= io.pfrio_size)
1055			break;
1056
1057		numtbls = io.pfrio_size;
1058	}
1059
1060	for (i = 0; i < numtbls; i++) {
1061		e = malloc(sizeof(struct pfr_tstats));
1062		e->index = i + 1;
1063		memcpy(&e->pft, t+i, sizeof(struct pfr_tstats));
1064		TAILQ_INSERT_TAIL(&pft_table, e, link);
1065	}
1066
1067	pft_table_age = time(NULL);
1068	pft_table_count = numtbls;
1069	pf_tick = this_tick;
1070
1071	free(t);
1072	return (0);
1073}
1074
1075/*
1076 * Implement the bsnmpd module interface
1077 */
1078static int
1079pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[])
1080{
1081	module = mod;
1082
1083	if ((dev = open("/dev/pf", O_RDONLY)) == -1) {
1084		syslog(LOG_ERR, "pf_init(): open(): %s\n",
1085		    strerror(errno));
1086		return (-1);
1087	}
1088
1089	/* Prepare internal state */
1090	TAILQ_INIT(&pfi_table);
1091	TAILQ_INIT(&pfq_table);
1092	TAILQ_INIT(&pft_table);
1093
1094	pfi_refresh();
1095	pfq_refresh();
1096	pfs_refresh();
1097	pft_refresh();
1098
1099	started = 1;
1100
1101	return (0);
1102}
1103
1104static int
1105pf_fini(void)
1106{
1107	struct pfi_entry *i1, *i2;
1108	struct pfq_entry *q1, *q2;
1109	struct pft_entry *t1, *t2;
1110
1111	/* Empty the list of interfaces */
1112	i1 = TAILQ_FIRST(&pfi_table);
1113	while (i1 != NULL) {
1114		i2 = TAILQ_NEXT(i1, link);
1115		free(i1);
1116		i1 = i2;
1117	}
1118
1119	/* List of queues */
1120	q1 = TAILQ_FIRST(&pfq_table);
1121	while (q1 != NULL) {
1122		q2 = TAILQ_NEXT(q1, link);
1123		free(q1);
1124		q1 = q2;
1125	}
1126
1127	/* And the list of tables */
1128	t1 = TAILQ_FIRST(&pft_table);
1129	while (t1 != NULL) {
1130		t2 = TAILQ_NEXT(t1, link);
1131		free(t1);
1132		t1 = t2;
1133	}
1134
1135	close(dev);
1136	return (0);
1137}
1138
1139static void
1140pf_dump(void)
1141{
1142	pfi_refresh();
1143	pfq_refresh();
1144	pft_refresh();
1145
1146	syslog(LOG_ERR, "Dump: pfi_table_age = %u",
1147	    pfi_table_age);
1148	syslog(LOG_ERR, "Dump: pfi_table_count = %d",
1149	    pfi_table_count);
1150
1151	syslog(LOG_ERR, "Dump: pfq_table_age = %u",
1152	    pfq_table_age);
1153	syslog(LOG_ERR, "Dump: pfq_table_count = %d",
1154	    pfq_table_count);
1155
1156	syslog(LOG_ERR, "Dump: pft_table_age = %u",
1157	    pft_table_age);
1158
1159	syslog(LOG_ERR, "Dump: pft_table_count = %d",
1160	    pft_table_count);
1161}
1162
1163const struct snmp_module config = {
1164	.comment = "This module implements a MIB for the pf packet filter.",
1165	.init =		pf_init,
1166	.fini =		pf_fini,
1167	.tree =		pf_ctree,
1168	.dump =		pf_dump,
1169	.tree_size =	pf_CTREE_SIZE,
1170};
1171