pf_snmp.c revision 205280
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 205280 2010-03-18 14:54:31Z syrinx $
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 <stdint.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <syslog.h>
41#include <unistd.h>
42
43#include "pf_oid.h"
44#include "pf_tree.h"
45
46struct lmodule *module;
47
48static int dev = -1;
49static int started;
50static uint64_t pf_tick;
51
52static struct pf_status pfs;
53
54enum { IN, OUT };
55enum { IPV4, IPV6 };
56enum { PASS, BLOCK };
57
58#define PFI_IFTYPE_GROUP	0
59#define PFI_IFTYPE_INSTANCE	1
60#define PFI_IFTYPE_DETACHED	2
61
62struct pfi_entry {
63	struct pfi_kif	pfi;
64	u_int		index;
65	TAILQ_ENTRY(pfi_entry) link;
66};
67TAILQ_HEAD(pfi_table, pfi_entry);
68
69static struct pfi_table pfi_table;
70static time_t pfi_table_age;
71static int pfi_table_count;
72
73#define PFI_TABLE_MAXAGE	5
74
75struct pft_entry {
76	struct pfr_tstats pft;
77	u_int		index;
78	TAILQ_ENTRY(pft_entry) link;
79};
80TAILQ_HEAD(pft_table, pft_entry);
81
82static struct pft_table pft_table;
83static time_t pft_table_age;
84static int pft_table_count;
85
86#define PFT_TABLE_MAXAGE	5
87
88struct pfq_entry {
89	struct pf_altq	altq;
90	u_int		index;
91	TAILQ_ENTRY(pfq_entry) link;
92};
93TAILQ_HEAD(pfq_table, pfq_entry);
94
95static struct pfq_table pfq_table;
96static time_t pfq_table_age;
97static int pfq_table_count;
98
99static int altq_enabled = 0;
100
101#define PFQ_TABLE_MAXAGE	5
102
103struct pfl_entry {
104	char		name[MAXPATHLEN + PF_RULE_LABEL_SIZE];
105	u_int64_t	evals;
106	u_int64_t	bytes[2];
107	u_int64_t	pkts[2];
108	u_int		index;
109	TAILQ_ENTRY(pfl_entry) link;
110};
111TAILQ_HEAD(pfl_table, pfl_entry);
112
113static struct pfl_table pfl_table;
114static time_t pfl_table_age;
115static int pfl_table_count;
116
117#define	PFL_TABLE_MAXAGE	5
118
119/* Forward declarations */
120static int pfi_refresh(void);
121static int pfq_refresh(void);
122static int pfs_refresh(void);
123static int pft_refresh(void);
124static int pfl_refresh(void);
125static struct pfi_entry * pfi_table_find(u_int idx);
126static struct pfq_entry * pfq_table_find(u_int idx);
127static struct pft_entry * pft_table_find(u_int idx);
128static struct pfl_entry * pfl_table_find(u_int idx);
129
130static int altq_is_enabled(int pfdevice);
131
132int
133pf_status(struct snmp_context __unused *ctx, struct snmp_value *val,
134	u_int sub, u_int __unused vindex, enum snmp_op op)
135{
136	asn_subid_t	which = val->var.subs[sub - 1];
137	time_t		runtime;
138	unsigned char	str[128];
139
140	if (op == SNMP_OP_SET)
141		return (SNMP_ERR_NOT_WRITEABLE);
142
143	if (op == SNMP_OP_GET) {
144		if (pfs_refresh() == -1)
145			return (SNMP_ERR_GENERR);
146
147		switch (which) {
148			case LEAF_pfStatusRunning:
149			    val->v.uint32 = pfs.running;
150			    break;
151			case LEAF_pfStatusRuntime:
152			    runtime = (pfs.since > 0) ?
153				time(NULL) - pfs.since : 0;
154			    val->v.uint32 = runtime * 100;
155			    break;
156			case LEAF_pfStatusDebug:
157			    val->v.uint32 = pfs.debug;
158			    break;
159			case LEAF_pfStatusHostId:
160			    sprintf(str, "0x%08x", ntohl(pfs.hostid));
161			    return (string_get(val, str, strlen(str)));
162
163			default:
164			    return (SNMP_ERR_NOSUCHNAME);
165		}
166
167		return (SNMP_ERR_NOERROR);
168	}
169
170	abort();
171}
172
173int
174pf_counter(struct snmp_context __unused *ctx, struct snmp_value *val,
175	u_int sub, u_int __unused vindex, enum snmp_op op)
176{
177	asn_subid_t	which = val->var.subs[sub - 1];
178
179	if (op == SNMP_OP_SET)
180		return (SNMP_ERR_NOT_WRITEABLE);
181
182	if (op == SNMP_OP_GET) {
183		if (pfs_refresh() == -1)
184			return (SNMP_ERR_GENERR);
185
186		switch (which) {
187			case LEAF_pfCounterMatch:
188				val->v.counter64 = pfs.counters[PFRES_MATCH];
189				break;
190			case LEAF_pfCounterBadOffset:
191				val->v.counter64 = pfs.counters[PFRES_BADOFF];
192				break;
193			case LEAF_pfCounterFragment:
194				val->v.counter64 = pfs.counters[PFRES_FRAG];
195				break;
196			case LEAF_pfCounterShort:
197				val->v.counter64 = pfs.counters[PFRES_SHORT];
198				break;
199			case LEAF_pfCounterNormalize:
200				val->v.counter64 = pfs.counters[PFRES_NORM];
201				break;
202			case LEAF_pfCounterMemDrop:
203				val->v.counter64 = pfs.counters[PFRES_MEMORY];
204				break;
205
206			default:
207				return (SNMP_ERR_NOSUCHNAME);
208		}
209
210		return (SNMP_ERR_NOERROR);
211	}
212
213	abort();
214}
215
216int
217pf_statetable(struct snmp_context __unused *ctx, struct snmp_value *val,
218	u_int sub, u_int __unused vindex, enum snmp_op op)
219{
220	asn_subid_t	which = val->var.subs[sub - 1];
221
222	if (op == SNMP_OP_SET)
223		return (SNMP_ERR_NOT_WRITEABLE);
224
225	if (op == SNMP_OP_GET) {
226		if (pfs_refresh() == -1)
227			return (SNMP_ERR_GENERR);
228
229		switch (which) {
230			case LEAF_pfStateTableCount:
231				val->v.uint32 = pfs.states;
232				break;
233			case LEAF_pfStateTableSearches:
234				val->v.counter64 =
235				    pfs.fcounters[FCNT_STATE_SEARCH];
236				break;
237			case LEAF_pfStateTableInserts:
238				val->v.counter64 =
239				    pfs.fcounters[FCNT_STATE_INSERT];
240				break;
241			case LEAF_pfStateTableRemovals:
242				val->v.counter64 =
243				    pfs.fcounters[FCNT_STATE_REMOVALS];
244				break;
245
246			default:
247				return (SNMP_ERR_NOSUCHNAME);
248		}
249
250		return (SNMP_ERR_NOERROR);
251	}
252
253	abort();
254}
255
256int
257pf_srcnodes(struct snmp_context __unused *ctx, struct snmp_value *val,
258	u_int sub, u_int __unused vindex, enum snmp_op op)
259{
260	asn_subid_t	which = val->var.subs[sub - 1];
261
262	if (op == SNMP_OP_SET)
263		return (SNMP_ERR_NOT_WRITEABLE);
264
265	if (op == SNMP_OP_GET) {
266		if (pfs_refresh() == -1)
267			return (SNMP_ERR_GENERR);
268
269		switch (which) {
270			case LEAF_pfSrcNodesCount:
271				val->v.uint32 = pfs.src_nodes;
272				break;
273			case LEAF_pfSrcNodesSearches:
274				val->v.counter64 =
275				    pfs.scounters[SCNT_SRC_NODE_SEARCH];
276				break;
277			case LEAF_pfSrcNodesInserts:
278				val->v.counter64 =
279				    pfs.scounters[SCNT_SRC_NODE_INSERT];
280				break;
281			case LEAF_pfSrcNodesRemovals:
282				val->v.counter64 =
283				    pfs.scounters[SCNT_SRC_NODE_REMOVALS];
284				break;
285
286			default:
287				return (SNMP_ERR_NOSUCHNAME);
288		}
289
290		return (SNMP_ERR_NOERROR);
291	}
292
293	abort();
294}
295
296int
297pf_limits(struct snmp_context __unused *ctx, struct snmp_value *val,
298	u_int sub, u_int __unused vindex, enum snmp_op op)
299{
300	asn_subid_t		which = val->var.subs[sub - 1];
301	struct pfioc_limit	pl;
302
303	if (op == SNMP_OP_SET)
304		return (SNMP_ERR_NOT_WRITEABLE);
305
306	if (op == SNMP_OP_GET) {
307		bzero(&pl, sizeof(struct pfioc_limit));
308
309		switch (which) {
310			case LEAF_pfLimitsStates:
311				pl.index = PF_LIMIT_STATES;
312				break;
313			case LEAF_pfLimitsSrcNodes:
314				pl.index = PF_LIMIT_SRC_NODES;
315				break;
316			case LEAF_pfLimitsFrags:
317				pl.index = PF_LIMIT_FRAGS;
318				break;
319
320			default:
321				return (SNMP_ERR_NOSUCHNAME);
322		}
323
324		if (ioctl(dev, DIOCGETLIMIT, &pl)) {
325			syslog(LOG_ERR, "pf_limits(): ioctl(): %s",
326			    strerror(errno));
327			return (SNMP_ERR_GENERR);
328		}
329
330		val->v.uint32 = pl.limit;
331
332		return (SNMP_ERR_NOERROR);
333	}
334
335	abort();
336}
337
338int
339pf_timeouts(struct snmp_context __unused *ctx, struct snmp_value *val,
340	u_int sub, u_int __unused vindex, enum snmp_op op)
341{
342	asn_subid_t	which = val->var.subs[sub - 1];
343	struct pfioc_tm	pt;
344
345	if (op == SNMP_OP_SET)
346		return (SNMP_ERR_NOT_WRITEABLE);
347
348	if (op == SNMP_OP_GET) {
349		bzero(&pt, sizeof(struct pfioc_tm));
350
351		switch (which) {
352			case LEAF_pfTimeoutsTcpFirst:
353				pt.timeout = PFTM_TCP_FIRST_PACKET;
354				break;
355			case LEAF_pfTimeoutsTcpOpening:
356				pt.timeout = PFTM_TCP_OPENING;
357				break;
358			case LEAF_pfTimeoutsTcpEstablished:
359				pt.timeout = PFTM_TCP_ESTABLISHED;
360				break;
361			case LEAF_pfTimeoutsTcpClosing:
362				pt.timeout = PFTM_TCP_CLOSING;
363				break;
364			case LEAF_pfTimeoutsTcpFinWait:
365				pt.timeout = PFTM_TCP_FIN_WAIT;
366				break;
367			case LEAF_pfTimeoutsTcpClosed:
368				pt.timeout = PFTM_TCP_CLOSED;
369				break;
370			case LEAF_pfTimeoutsUdpFirst:
371				pt.timeout = PFTM_UDP_FIRST_PACKET;
372				break;
373			case LEAF_pfTimeoutsUdpSingle:
374				pt.timeout = PFTM_UDP_SINGLE;
375				break;
376			case LEAF_pfTimeoutsUdpMultiple:
377				pt.timeout = PFTM_UDP_MULTIPLE;
378				break;
379			case LEAF_pfTimeoutsIcmpFirst:
380				pt.timeout = PFTM_ICMP_FIRST_PACKET;
381				break;
382			case LEAF_pfTimeoutsIcmpError:
383				pt.timeout = PFTM_ICMP_ERROR_REPLY;
384				break;
385			case LEAF_pfTimeoutsOtherFirst:
386				pt.timeout = PFTM_OTHER_FIRST_PACKET;
387				break;
388			case LEAF_pfTimeoutsOtherSingle:
389				pt.timeout = PFTM_OTHER_SINGLE;
390				break;
391			case LEAF_pfTimeoutsOtherMultiple:
392				pt.timeout = PFTM_OTHER_MULTIPLE;
393				break;
394			case LEAF_pfTimeoutsFragment:
395				pt.timeout = PFTM_FRAG;
396				break;
397			case LEAF_pfTimeoutsInterval:
398				pt.timeout = PFTM_INTERVAL;
399				break;
400			case LEAF_pfTimeoutsAdaptiveStart:
401				pt.timeout = PFTM_ADAPTIVE_START;
402				break;
403			case LEAF_pfTimeoutsAdaptiveEnd:
404				pt.timeout = PFTM_ADAPTIVE_END;
405				break;
406			case LEAF_pfTimeoutsSrcNode:
407				pt.timeout = PFTM_SRC_NODE;
408				break;
409
410			default:
411				return (SNMP_ERR_NOSUCHNAME);
412		}
413
414		if (ioctl(dev, DIOCGETTIMEOUT, &pt)) {
415			syslog(LOG_ERR, "pf_timeouts(): ioctl(): %s",
416			    strerror(errno));
417			return (SNMP_ERR_GENERR);
418		}
419
420		val->v.integer = pt.seconds;
421
422		return (SNMP_ERR_NOERROR);
423	}
424
425	abort();
426}
427
428int
429pf_logif(struct snmp_context __unused *ctx, struct snmp_value *val,
430	u_int sub, u_int __unused vindex, enum snmp_op op)
431{
432	asn_subid_t	which = val->var.subs[sub - 1];
433	unsigned char	str[IFNAMSIZ];
434
435	if (op == SNMP_OP_SET)
436		return (SNMP_ERR_NOT_WRITEABLE);
437
438	if (op == SNMP_OP_GET) {
439		if (pfs_refresh() == -1)
440			return (SNMP_ERR_GENERR);
441
442		switch (which) {
443	 		case LEAF_pfLogInterfaceName:
444				strlcpy(str, pfs.ifname, sizeof str);
445				return (string_get(val, str, strlen(str)));
446			case LEAF_pfLogInterfaceIp4BytesIn:
447				val->v.counter64 = pfs.bcounters[IPV4][IN];
448				break;
449			case LEAF_pfLogInterfaceIp4BytesOut:
450				val->v.counter64 = pfs.bcounters[IPV4][OUT];
451				break;
452			case LEAF_pfLogInterfaceIp4PktsInPass:
453				val->v.counter64 =
454				    pfs.pcounters[IPV4][IN][PF_PASS];
455				break;
456			case LEAF_pfLogInterfaceIp4PktsInDrop:
457				val->v.counter64 =
458				    pfs.pcounters[IPV4][IN][PF_DROP];
459				break;
460			case LEAF_pfLogInterfaceIp4PktsOutPass:
461				val->v.counter64 =
462				    pfs.pcounters[IPV4][OUT][PF_PASS];
463				break;
464			case LEAF_pfLogInterfaceIp4PktsOutDrop:
465				val->v.counter64 =
466				    pfs.pcounters[IPV4][OUT][PF_DROP];
467				break;
468			case LEAF_pfLogInterfaceIp6BytesIn:
469				val->v.counter64 = pfs.bcounters[IPV6][IN];
470				break;
471			case LEAF_pfLogInterfaceIp6BytesOut:
472				val->v.counter64 = pfs.bcounters[IPV6][OUT];
473				break;
474			case LEAF_pfLogInterfaceIp6PktsInPass:
475				val->v.counter64 =
476				    pfs.pcounters[IPV6][IN][PF_PASS];
477				break;
478			case LEAF_pfLogInterfaceIp6PktsInDrop:
479				val->v.counter64 =
480				    pfs.pcounters[IPV6][IN][PF_DROP];
481				break;
482			case LEAF_pfLogInterfaceIp6PktsOutPass:
483				val->v.counter64 =
484				    pfs.pcounters[IPV6][OUT][PF_PASS];
485				break;
486			case LEAF_pfLogInterfaceIp6PktsOutDrop:
487				val->v.counter64 =
488				    pfs.pcounters[IPV6][OUT][PF_DROP];
489				break;
490
491			default:
492				return (SNMP_ERR_NOSUCHNAME);
493		}
494
495		return (SNMP_ERR_NOERROR);
496	}
497
498	abort();
499}
500
501int
502pf_interfaces(struct snmp_context __unused *ctx, struct snmp_value *val,
503	u_int sub, u_int __unused vindex, enum snmp_op op)
504{
505	asn_subid_t	which = val->var.subs[sub - 1];
506
507	if (op == SNMP_OP_SET)
508		return (SNMP_ERR_NOT_WRITEABLE);
509
510	if (op == SNMP_OP_GET) {
511		if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
512			if (pfi_refresh() == -1)
513			    return (SNMP_ERR_GENERR);
514
515		switch (which) {
516			case LEAF_pfInterfacesIfNumber:
517				val->v.uint32 = pfi_table_count;
518				break;
519
520			default:
521				return (SNMP_ERR_NOSUCHNAME);
522		}
523
524		return (SNMP_ERR_NOERROR);
525	}
526
527	abort();
528}
529
530int
531pf_iftable(struct snmp_context __unused *ctx, struct snmp_value *val,
532	u_int sub, u_int __unused vindex, enum snmp_op op)
533{
534	asn_subid_t	which = val->var.subs[sub - 1];
535	struct pfi_entry *e = NULL;
536
537	switch (op) {
538		case SNMP_OP_SET:
539			return (SNMP_ERR_NOT_WRITEABLE);
540		case SNMP_OP_GETNEXT:
541			if ((e = NEXT_OBJECT_INT(&pfi_table,
542			    &val->var, sub)) == NULL)
543				return (SNMP_ERR_NOSUCHNAME);
544			val->var.len = sub + 1;
545			val->var.subs[sub] = e->index;
546			break;
547		case SNMP_OP_GET:
548			if (val->var.len - sub != 1)
549				return (SNMP_ERR_NOSUCHNAME);
550			if ((e = pfi_table_find(val->var.subs[sub])) == NULL)
551				return (SNMP_ERR_NOSUCHNAME);
552			break;
553
554		case SNMP_OP_COMMIT:
555		case SNMP_OP_ROLLBACK:
556		default:
557			abort();
558	}
559
560	if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
561		pfi_refresh();
562
563	switch (which) {
564		case LEAF_pfInterfacesIfDescr:
565			return (string_get(val, e->pfi.pfik_name, -1));
566		case LEAF_pfInterfacesIfType:
567			val->v.integer = PFI_IFTYPE_INSTANCE;
568			break;
569		case LEAF_pfInterfacesIfTZero:
570			val->v.uint32 =
571			    (time(NULL) - e->pfi.pfik_tzero) * 100;
572			break;
573		case LEAF_pfInterfacesIfRefsState:
574			val->v.uint32 = e->pfi.pfik_states;
575			break;
576		case LEAF_pfInterfacesIfRefsRule:
577			val->v.uint32 = e->pfi.pfik_rules;
578			break;
579		case LEAF_pfInterfacesIf4BytesInPass:
580			val->v.counter64 =
581			    e->pfi.pfik_bytes[IPV4][IN][PASS];
582			break;
583		case LEAF_pfInterfacesIf4BytesInBlock:
584			val->v.counter64 =
585			    e->pfi.pfik_bytes[IPV4][IN][BLOCK];
586			break;
587		case LEAF_pfInterfacesIf4BytesOutPass:
588			val->v.counter64 =
589			    e->pfi.pfik_bytes[IPV4][OUT][PASS];
590			break;
591		case LEAF_pfInterfacesIf4BytesOutBlock:
592			val->v.counter64 =
593			    e->pfi.pfik_bytes[IPV4][OUT][BLOCK];
594			break;
595		case LEAF_pfInterfacesIf4PktsInPass:
596			val->v.counter64 =
597			    e->pfi.pfik_packets[IPV4][IN][PASS];
598			break;
599		case LEAF_pfInterfacesIf4PktsInBlock:
600			val->v.counter64 =
601			    e->pfi.pfik_packets[IPV4][IN][BLOCK];
602			break;
603		case LEAF_pfInterfacesIf4PktsOutPass:
604			val->v.counter64 =
605			    e->pfi.pfik_packets[IPV4][OUT][PASS];
606			break;
607		case LEAF_pfInterfacesIf4PktsOutBlock:
608			val->v.counter64 =
609			    e->pfi.pfik_packets[IPV4][OUT][BLOCK];
610			break;
611		case LEAF_pfInterfacesIf6BytesInPass:
612			val->v.counter64 =
613			    e->pfi.pfik_bytes[IPV6][IN][PASS];
614			break;
615		case LEAF_pfInterfacesIf6BytesInBlock:
616			val->v.counter64 =
617			    e->pfi.pfik_bytes[IPV6][IN][BLOCK];
618			break;
619		case LEAF_pfInterfacesIf6BytesOutPass:
620			val->v.counter64 =
621			    e->pfi.pfik_bytes[IPV6][OUT][PASS];
622			break;
623		case LEAF_pfInterfacesIf6BytesOutBlock:
624			val->v.counter64 =
625			    e->pfi.pfik_bytes[IPV6][OUT][BLOCK];
626			break;
627		case LEAF_pfInterfacesIf6PktsInPass:
628			val->v.counter64 =
629			    e->pfi.pfik_packets[IPV6][IN][PASS];
630			break;
631		case LEAF_pfInterfacesIf6PktsInBlock:
632			val->v.counter64 =
633			    e->pfi.pfik_packets[IPV6][IN][BLOCK];
634			break;
635		case LEAF_pfInterfacesIf6PktsOutPass:
636			val->v.counter64 =
637			    e->pfi.pfik_packets[IPV6][OUT][PASS];
638			break;
639		case LEAF_pfInterfacesIf6PktsOutBlock:
640			val->v.counter64 =
641			    e->pfi.pfik_packets[IPV6][OUT][BLOCK];
642			break;
643
644		default:
645			return (SNMP_ERR_NOSUCHNAME);
646	}
647
648	return (SNMP_ERR_NOERROR);
649}
650
651int
652pf_tables(struct snmp_context __unused *ctx, struct snmp_value *val,
653	u_int sub, u_int __unused vindex, enum snmp_op op)
654{
655	asn_subid_t	which = val->var.subs[sub - 1];
656
657	if (op == SNMP_OP_SET)
658		return (SNMP_ERR_NOT_WRITEABLE);
659
660	if (op == SNMP_OP_GET) {
661		if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
662			if (pft_refresh() == -1)
663			    return (SNMP_ERR_GENERR);
664
665		switch (which) {
666			case LEAF_pfTablesTblNumber:
667				val->v.uint32 = pft_table_count;
668				break;
669
670			default:
671				return (SNMP_ERR_NOSUCHNAME);
672		}
673
674		return (SNMP_ERR_NOERROR);
675	}
676
677	abort();
678}
679
680int
681pf_tbltable(struct snmp_context __unused *ctx, struct snmp_value *val,
682	u_int sub, u_int __unused vindex, enum snmp_op op)
683{
684	asn_subid_t	which = val->var.subs[sub - 1];
685	struct pft_entry *e = NULL;
686
687	switch (op) {
688		case SNMP_OP_SET:
689			return (SNMP_ERR_NOT_WRITEABLE);
690		case SNMP_OP_GETNEXT:
691			if ((e = NEXT_OBJECT_INT(&pft_table,
692			    &val->var, sub)) == NULL)
693				return (SNMP_ERR_NOSUCHNAME);
694			val->var.len = sub + 1;
695			val->var.subs[sub] = e->index;
696			break;
697		case SNMP_OP_GET:
698			if (val->var.len - sub != 1)
699				return (SNMP_ERR_NOSUCHNAME);
700			if ((e = pft_table_find(val->var.subs[sub])) == NULL)
701				return (SNMP_ERR_NOSUCHNAME);
702			break;
703
704		case SNMP_OP_COMMIT:
705		case SNMP_OP_ROLLBACK:
706		default:
707			abort();
708	}
709
710	if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
711		pft_refresh();
712
713	switch (which) {
714		case LEAF_pfTablesTblDescr:
715			return (string_get(val, e->pft.pfrts_name, -1));
716		case LEAF_pfTablesTblCount:
717			val->v.integer = e->pft.pfrts_cnt;
718			break;
719		case LEAF_pfTablesTblTZero:
720			val->v.uint32 =
721			    (time(NULL) - e->pft.pfrts_tzero) * 100;
722			break;
723		case LEAF_pfTablesTblRefsAnchor:
724			val->v.integer =
725			    e->pft.pfrts_refcnt[PFR_REFCNT_ANCHOR];
726			break;
727		case LEAF_pfTablesTblRefsRule:
728			val->v.integer =
729			    e->pft.pfrts_refcnt[PFR_REFCNT_RULE];
730			break;
731		case LEAF_pfTablesTblEvalMatch:
732			val->v.counter64 = e->pft.pfrts_match;
733			break;
734		case LEAF_pfTablesTblEvalNoMatch:
735			val->v.counter64 = e->pft.pfrts_nomatch;
736			break;
737		case LEAF_pfTablesTblBytesInPass:
738			val->v.counter64 =
739			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_PASS];
740			break;
741		case LEAF_pfTablesTblBytesInBlock:
742			val->v.counter64 =
743			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_BLOCK];
744			break;
745		case LEAF_pfTablesTblBytesInXPass:
746			val->v.counter64 =
747			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_XPASS];
748			break;
749		case LEAF_pfTablesTblBytesOutPass:
750			val->v.counter64 =
751			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_PASS];
752			break;
753		case LEAF_pfTablesTblBytesOutBlock:
754			val->v.counter64 =
755			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_BLOCK];
756			break;
757		case LEAF_pfTablesTblBytesOutXPass:
758			val->v.counter64 =
759			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_XPASS];
760			break;
761		case LEAF_pfTablesTblPktsInPass:
762			val->v.counter64 =
763			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_PASS];
764			break;
765		case LEAF_pfTablesTblPktsInBlock:
766			val->v.counter64 =
767			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_BLOCK];
768			break;
769		case LEAF_pfTablesTblPktsInXPass:
770			val->v.counter64 =
771			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_XPASS];
772			break;
773		case LEAF_pfTablesTblPktsOutPass:
774			val->v.counter64 =
775			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_PASS];
776			break;
777		case LEAF_pfTablesTblPktsOutBlock:
778			val->v.counter64 =
779			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_BLOCK];
780			break;
781		case LEAF_pfTablesTblPktsOutXPass:
782			val->v.counter64 =
783			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_XPASS];
784			break;
785
786		default:
787			return (SNMP_ERR_NOSUCHNAME);
788	}
789
790	return (SNMP_ERR_NOERROR);
791}
792
793int
794pf_tbladdr(struct snmp_context __unused *ctx, struct snmp_value __unused *val,
795	u_int __unused sub, u_int __unused vindex, enum snmp_op __unused op)
796{
797	return (SNMP_ERR_GENERR);
798}
799
800int
801pf_altq(struct snmp_context __unused *ctx, struct snmp_value *val,
802	u_int sub, u_int __unused vindex, enum snmp_op op)
803{
804	asn_subid_t	which = val->var.subs[sub - 1];
805
806	if (!altq_enabled) {
807	   return (SNMP_ERR_NOERROR);
808	}
809
810	if (op == SNMP_OP_SET)
811		return (SNMP_ERR_NOT_WRITEABLE);
812
813	if (op == SNMP_OP_GET) {
814		if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
815			if (pfq_refresh() == -1)
816			    return (SNMP_ERR_GENERR);
817
818		switch (which) {
819			case LEAF_pfAltqQueueNumber:
820				val->v.uint32 = pfq_table_count;
821				break;
822
823			default:
824				return (SNMP_ERR_NOSUCHNAME);
825		}
826
827		return (SNMP_ERR_NOERROR);
828	}
829
830	abort();
831	return (SNMP_ERR_GENERR);
832}
833
834int
835pf_altqq(struct snmp_context __unused *ctx, struct snmp_value *val,
836	u_int sub, u_int __unused vindex, enum snmp_op op)
837{
838	asn_subid_t	which = val->var.subs[sub - 1];
839	struct pfq_entry *e = NULL;
840
841	if (!altq_enabled) {
842	   return (SNMP_ERR_NOERROR);
843	}
844
845	switch (op) {
846		case SNMP_OP_SET:
847			return (SNMP_ERR_NOT_WRITEABLE);
848		case SNMP_OP_GETNEXT:
849			if ((e = NEXT_OBJECT_INT(&pfq_table,
850			    &val->var, sub)) == NULL)
851				return (SNMP_ERR_NOSUCHNAME);
852			val->var.len = sub + 1;
853			val->var.subs[sub] = e->index;
854			break;
855		case SNMP_OP_GET:
856			if (val->var.len - sub != 1)
857				return (SNMP_ERR_NOSUCHNAME);
858			if ((e = pfq_table_find(val->var.subs[sub])) == NULL)
859				return (SNMP_ERR_NOSUCHNAME);
860			break;
861
862		case SNMP_OP_COMMIT:
863		case SNMP_OP_ROLLBACK:
864		default:
865			abort();
866	}
867
868	if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
869		pfq_refresh();
870
871	switch (which) {
872		case LEAF_pfAltqQueueDescr:
873			return (string_get(val, e->altq.qname, -1));
874		case LEAF_pfAltqQueueParent:
875			return (string_get(val, e->altq.parent, -1));
876		case LEAF_pfAltqQueueScheduler:
877			val->v.integer = e->altq.scheduler;
878			break;
879		case LEAF_pfAltqQueueBandwidth:
880			val->v.uint32 = e->altq.bandwidth;
881			break;
882		case LEAF_pfAltqQueuePriority:
883			val->v.integer = e->altq.priority;
884			break;
885		case LEAF_pfAltqQueueLimit:
886			val->v.integer = e->altq.qlimit;
887			break;
888
889		default:
890			return (SNMP_ERR_NOSUCHNAME);
891	}
892
893	return (SNMP_ERR_NOERROR);
894}
895
896int
897pf_labels(struct snmp_context __unused *ctx, struct snmp_value *val,
898	u_int sub, u_int __unused vindex, enum snmp_op op)
899{
900	asn_subid_t	which = val->var.subs[sub - 1];
901
902	if (op == SNMP_OP_SET)
903		return (SNMP_ERR_NOT_WRITEABLE);
904
905	if (op == SNMP_OP_GET) {
906		if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE)
907			if (pfl_refresh() == -1)
908				return (SNMP_ERR_GENERR);
909
910		switch (which) {
911			case LEAF_pfLabelsLblNumber:
912				val->v.uint32 = pfl_table_count;
913				break;
914
915			default:
916				return (SNMP_ERR_NOSUCHNAME);
917		}
918
919		return (SNMP_ERR_NOERROR);
920	}
921
922	abort();
923	return (SNMP_ERR_GENERR);
924}
925
926int
927pf_lbltable(struct snmp_context __unused *ctx, struct snmp_value *val,
928	u_int sub, u_int __unused vindex, enum snmp_op op)
929{
930	asn_subid_t	which = val->var.subs[sub - 1];
931	struct pfl_entry *e = NULL;
932
933	switch (op) {
934		case SNMP_OP_SET:
935			return (SNMP_ERR_NOT_WRITEABLE);
936		case SNMP_OP_GETNEXT:
937			if ((e = NEXT_OBJECT_INT(&pfl_table,
938			    &val->var, sub)) == NULL)
939				return (SNMP_ERR_NOSUCHNAME);
940			val->var.len = sub + 1;
941			val->var.subs[sub] = e->index;
942			break;
943		case SNMP_OP_GET:
944			if (val->var.len - sub != 1)
945				return (SNMP_ERR_NOSUCHNAME);
946			if ((e = pfl_table_find(val->var.subs[sub])) == NULL)
947				return (SNMP_ERR_NOSUCHNAME);
948			break;
949
950		case SNMP_OP_COMMIT:
951		case SNMP_OP_ROLLBACK:
952		default:
953			abort();
954	}
955
956	if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE)
957		pfl_refresh();
958
959	switch (which) {
960		case LEAF_pfLabelsLblName:
961			return (string_get(val, e->name, -1));
962		case LEAF_pfLabelsLblEvals:
963			val->v.counter64 = e->evals;
964			break;
965		case LEAF_pfLabelsLblBytesIn:
966			val->v.counter64 = e->bytes[IN];
967			break;
968		case LEAF_pfLabelsLblBytesOut:
969			val->v.counter64 = e->bytes[OUT];
970			break;
971		case LEAF_pfLabelsLblPktsIn:
972			val->v.counter64 = e->pkts[IN];
973			break;
974		case LEAF_pfLabelsLblPktsOut:
975			val->v.counter64 = e->pkts[OUT];
976			break;
977		default:
978			return (SNMP_ERR_NOSUCHNAME);
979	}
980
981	return (SNMP_ERR_NOERROR);
982}
983
984static struct pfi_entry *
985pfi_table_find(u_int idx)
986{
987	struct pfi_entry *e;
988
989	TAILQ_FOREACH(e, &pfi_table, link)
990		if (e->index == idx)
991			return (e);
992	return (NULL);
993}
994
995static struct pfq_entry *
996pfq_table_find(u_int idx)
997{
998	struct pfq_entry *e;
999
1000	TAILQ_FOREACH(e, &pfq_table, link)
1001		if (e->index == idx)
1002			return (e);
1003	return (NULL);
1004}
1005
1006static struct pft_entry *
1007pft_table_find(u_int idx)
1008{
1009	struct pft_entry *e;
1010
1011	TAILQ_FOREACH(e, &pft_table, link)
1012		if (e->index == idx)
1013			return (e);
1014	return (NULL);
1015}
1016
1017static struct pfl_entry *
1018pfl_table_find(u_int idx)
1019{
1020	struct pfl_entry *e;
1021
1022	TAILQ_FOREACH(e, &pfl_table, link)
1023		if (e->index == idx)
1024			return (e);
1025
1026	return (NULL);
1027}
1028
1029static int
1030pfi_refresh(void)
1031{
1032	struct pfioc_iface io;
1033	struct pfi_kif *p = NULL;
1034	struct pfi_entry *e;
1035	int i, numifs = 1;
1036
1037	if (started && this_tick <= pf_tick)
1038		return (0);
1039
1040	while (!TAILQ_EMPTY(&pfi_table)) {
1041		e = TAILQ_FIRST(&pfi_table);
1042		TAILQ_REMOVE(&pfi_table, e, link);
1043		free(e);
1044	}
1045
1046	bzero(&io, sizeof(io));
1047	io.pfiio_esize = sizeof(struct pfi_kif);
1048
1049	for (;;) {
1050		p = reallocf(p, numifs * sizeof(struct pfi_kif));
1051		if (p == NULL) {
1052			syslog(LOG_ERR, "pfi_refresh(): reallocf() numifs=%d: %s",
1053			    numifs, strerror(errno));
1054			goto err2;
1055		}
1056		io.pfiio_size = numifs;
1057		io.pfiio_buffer = p;
1058
1059		if (ioctl(dev, DIOCIGETIFACES, &io)) {
1060			syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s",
1061			    strerror(errno));
1062			goto err2;
1063		}
1064
1065		if (numifs >= io.pfiio_size)
1066			break;
1067
1068		numifs = io.pfiio_size;
1069	}
1070
1071	for (i = 0; i < numifs; i++) {
1072		e = malloc(sizeof(struct pfi_entry));
1073		if (e == NULL)
1074			goto err1;
1075		e->index = i + 1;
1076		memcpy(&e->pfi, p+i, sizeof(struct pfi_kif));
1077		TAILQ_INSERT_TAIL(&pfi_table, e, link);
1078	}
1079
1080	pfi_table_age = time(NULL);
1081	pfi_table_count = numifs;
1082	pf_tick = this_tick;
1083
1084	free(p);
1085	return (0);
1086
1087err1:
1088	while (!TAILQ_EMPTY(&pfi_table)) {
1089		e = TAILQ_FIRST(&pfi_table);
1090		TAILQ_REMOVE(&pfi_table, e, link);
1091		free(e);
1092	}
1093err2:
1094	free(p);
1095	return(-1);
1096}
1097
1098static int
1099pfq_refresh(void)
1100{
1101	struct pfioc_altq pa;
1102	struct pfq_entry *e;
1103	int i, numqs, ticket;
1104
1105	if (started && this_tick <= pf_tick)
1106		return (0);
1107
1108	while (!TAILQ_EMPTY(&pfq_table)) {
1109		e = TAILQ_FIRST(&pfq_table);
1110		TAILQ_REMOVE(&pfq_table, e, link);
1111		free(e);
1112	}
1113
1114	bzero(&pa, sizeof(pa));
1115
1116	if (ioctl(dev, DIOCGETALTQS, &pa)) {
1117		syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s",
1118		    strerror(errno));
1119		return (-1);
1120	}
1121
1122	numqs = pa.nr;
1123	ticket = pa.ticket;
1124
1125	for (i = 0; i < numqs; i++) {
1126		e = malloc(sizeof(struct pfq_entry));
1127		if (e == NULL) {
1128			syslog(LOG_ERR, "pfq_refresh(): "
1129			    "malloc(): %s",
1130			    strerror(errno));
1131			goto err;
1132		}
1133		pa.ticket = ticket;
1134		pa.nr = i;
1135
1136		if (ioctl(dev, DIOCGETALTQ, &pa)) {
1137			syslog(LOG_ERR, "pfq_refresh(): "
1138			    "ioctl(DIOCGETALTQ): %s",
1139			    strerror(errno));
1140			goto err;
1141		}
1142
1143		if (pa.altq.qid > 0) {
1144			memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq));
1145			e->index = pa.altq.qid;
1146			pfq_table_count = i;
1147			INSERT_OBJECT_INT_LINK_INDEX(e, &pfq_table, link, index);
1148		}
1149	}
1150
1151	pfq_table_age = time(NULL);
1152	pf_tick = this_tick;
1153
1154	return (0);
1155err:
1156	free(e);
1157	while (!TAILQ_EMPTY(&pfq_table)) {
1158		e = TAILQ_FIRST(&pfq_table);
1159		TAILQ_REMOVE(&pfq_table, e, link);
1160		free(e);
1161	}
1162	return(-1);
1163}
1164
1165static int
1166pfs_refresh(void)
1167{
1168	if (started && this_tick <= pf_tick)
1169		return (0);
1170
1171	bzero(&pfs, sizeof(struct pf_status));
1172
1173	if (ioctl(dev, DIOCGETSTATUS, &pfs)) {
1174		syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s",
1175		    strerror(errno));
1176		return (-1);
1177	}
1178
1179	pf_tick = this_tick;
1180	return (0);
1181}
1182
1183static int
1184pft_refresh(void)
1185{
1186	struct pfioc_table io;
1187	struct pfr_tstats *t = NULL;
1188	struct pft_entry *e;
1189	int i, numtbls = 1;
1190
1191	if (started && this_tick <= pf_tick)
1192		return (0);
1193
1194	while (!TAILQ_EMPTY(&pft_table)) {
1195		e = TAILQ_FIRST(&pft_table);
1196		TAILQ_REMOVE(&pft_table, e, link);
1197		free(e);
1198	}
1199
1200	bzero(&io, sizeof(io));
1201	io.pfrio_esize = sizeof(struct pfr_tstats);
1202
1203	for (;;) {
1204		t = reallocf(t, numtbls * sizeof(struct pfr_tstats));
1205		if (t == NULL) {
1206			syslog(LOG_ERR, "pft_refresh(): reallocf() numtbls=%d: %s",
1207			    numtbls, strerror(errno));
1208			goto err2;
1209		}
1210		io.pfrio_size = numtbls;
1211		io.pfrio_buffer = t;
1212
1213		if (ioctl(dev, DIOCRGETTSTATS, &io)) {
1214			syslog(LOG_ERR, "pft_refresh(): ioctl(): %s",
1215			    strerror(errno));
1216			goto err2;
1217		}
1218
1219		if (numtbls >= io.pfrio_size)
1220			break;
1221
1222		numtbls = io.pfrio_size;
1223	}
1224
1225	for (i = 0; i < numtbls; i++) {
1226		e = malloc(sizeof(struct pft_entry));
1227		if (e == NULL)
1228			goto err1;
1229		e->index = i + 1;
1230		memcpy(&e->pft, t+i, sizeof(struct pfr_tstats));
1231		TAILQ_INSERT_TAIL(&pft_table, e, link);
1232	}
1233
1234	pft_table_age = time(NULL);
1235	pft_table_count = numtbls;
1236	pf_tick = this_tick;
1237
1238	free(t);
1239	return (0);
1240err1:
1241	while (!TAILQ_EMPTY(&pft_table)) {
1242		e = TAILQ_FIRST(&pft_table);
1243		TAILQ_REMOVE(&pft_table, e, link);
1244		free(e);
1245	}
1246err2:
1247	free(t);
1248	return(-1);
1249}
1250
1251static int
1252pfl_scan_ruleset(const char *path)
1253{
1254	struct pfioc_rule pr;
1255	struct pfl_entry *e;
1256	u_int32_t nr, i;
1257
1258	bzero(&pr, sizeof(pr));
1259	strlcpy(pr.anchor, path, sizeof(pr.anchor));
1260	pr.rule.action = PF_PASS;
1261	if (ioctl(dev, DIOCGETRULES, &pr)) {
1262		syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULES): %s",
1263		    strerror(errno));
1264		goto err;
1265	}
1266
1267	for (nr = pr.nr, i = 0; i < nr; i++) {
1268		pr.nr = i;
1269		if (ioctl(dev, DIOCGETRULE, &pr)) {
1270			syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULE):"
1271			    " %s", strerror(errno));
1272			goto err;
1273		}
1274
1275		if (pr.rule.label[0]) {
1276			e = (struct pfl_entry *)malloc(sizeof(*e));
1277			if (e == NULL)
1278				goto err;
1279
1280			strlcpy(e->name, path, sizeof(e->name));
1281			if (path[0])
1282				strlcat(e->name, "/", sizeof(e->name));
1283			strlcat(e->name, pr.rule.label, sizeof(e->name));
1284
1285			e->evals = pr.rule.evaluations;
1286			e->bytes[IN] = pr.rule.bytes[IN];
1287			e->bytes[OUT] = pr.rule.bytes[OUT];
1288			e->pkts[IN] = pr.rule.packets[IN];
1289			e->pkts[OUT] = pr.rule.packets[OUT];
1290			e->index = ++pfl_table_count;
1291
1292			TAILQ_INSERT_TAIL(&pfl_table, e, link);
1293		}
1294	}
1295
1296	return (0);
1297
1298err:
1299	return (-1);
1300}
1301
1302static int
1303pfl_walk_rulesets(const char *path)
1304{
1305	struct pfioc_ruleset prs;
1306	char newpath[MAXPATHLEN];
1307	u_int32_t nr, i;
1308
1309	if (pfl_scan_ruleset(path))
1310		goto err;
1311
1312	bzero(&prs, sizeof(prs));
1313	strlcpy(prs.path, path, sizeof(prs.path));
1314	if (ioctl(dev, DIOCGETRULESETS, &prs)) {
1315		syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESETS): %s",
1316		    strerror(errno));
1317		goto err;
1318	}
1319
1320	for (nr = prs.nr, i = 0; i < nr; i++) {
1321		prs.nr = i;
1322		if (ioctl(dev, DIOCGETRULESET, &prs)) {
1323			syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESET):"
1324			    " %s", strerror(errno));
1325			goto err;
1326		}
1327
1328		if (strcmp(prs.name, PF_RESERVED_ANCHOR) == 0)
1329			continue;
1330
1331		strlcpy(newpath, path, sizeof(newpath));
1332		if (path[0])
1333			strlcat(newpath, "/", sizeof(newpath));
1334
1335		strlcat(newpath, prs.name, sizeof(newpath));
1336		if (pfl_walk_rulesets(newpath))
1337			goto err;
1338	}
1339
1340	return (0);
1341
1342err:
1343	return (-1);
1344}
1345
1346static int
1347pfl_refresh(void)
1348{
1349	struct pfl_entry *e;
1350
1351	if (started && this_tick <= pf_tick)
1352		return (0);
1353
1354	while (!TAILQ_EMPTY(&pfl_table)) {
1355		e = TAILQ_FIRST(&pfl_table);
1356		TAILQ_REMOVE(&pfl_table, e, link);
1357		free(e);
1358	}
1359	pfl_table_count = 0;
1360
1361	if (pfl_walk_rulesets(""))
1362		goto err;
1363
1364	pfl_table_age = time(NULL);
1365	pf_tick = this_tick;
1366
1367	return (0);
1368
1369err:
1370	while (!TAILQ_EMPTY(&pfl_table)) {
1371		e = TAILQ_FIRST(&pfl_table);
1372		TAILQ_REMOVE(&pfl_table, e, link);
1373		free(e);
1374	}
1375	pfl_table_count = 0;
1376
1377	return (-1);
1378}
1379
1380/*
1381 * check whether altq support is enabled in kernel
1382 */
1383
1384static int
1385altq_is_enabled(int pfdev)
1386{
1387        struct pfioc_altq pa;
1388
1389	errno = 0;
1390        if (ioctl(pfdev, DIOCGETALTQS, &pa)) {
1391                if (errno == ENODEV) {
1392			syslog(LOG_INFO, "No ALTQ support in kernel\n"
1393			    "ALTQ related functions disabled\n");
1394                        return (0);
1395                } else
1396                        syslog(LOG_ERR, "DIOCGETALTQS returned an error: %s",
1397			    strerror(errno));
1398			return (-1);
1399        }
1400        return (1);
1401}
1402
1403/*
1404 * Implement the bsnmpd module interface
1405 */
1406static int
1407pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[])
1408{
1409	module = mod;
1410
1411	if ((dev = open("/dev/pf", O_RDONLY)) == -1) {
1412		syslog(LOG_ERR, "pf_init(): open(): %s\n",
1413		    strerror(errno));
1414		return (-1);
1415	}
1416
1417	if ((altq_enabled = altq_is_enabled(dev)) == -1) {
1418		syslog(LOG_ERR, "pf_init(): altq test failed");
1419		return (-1);
1420	}
1421
1422	/* Prepare internal state */
1423	TAILQ_INIT(&pfi_table);
1424	TAILQ_INIT(&pfq_table);
1425	TAILQ_INIT(&pft_table);
1426	TAILQ_INIT(&pfl_table);
1427
1428	pfi_refresh();
1429	if (altq_enabled) {
1430		pfq_refresh();
1431	}
1432
1433	pfs_refresh();
1434	pft_refresh();
1435	pfl_refresh();
1436
1437	started = 1;
1438
1439	return (0);
1440}
1441
1442static int
1443pf_fini(void)
1444{
1445	struct pfi_entry *i1, *i2;
1446	struct pfq_entry *q1, *q2;
1447	struct pft_entry *t1, *t2;
1448	struct pfl_entry *l1, *l2;
1449
1450	/* Empty the list of interfaces */
1451	i1 = TAILQ_FIRST(&pfi_table);
1452	while (i1 != NULL) {
1453		i2 = TAILQ_NEXT(i1, link);
1454		free(i1);
1455		i1 = i2;
1456	}
1457
1458	/* List of queues */
1459	q1 = TAILQ_FIRST(&pfq_table);
1460	while (q1 != NULL) {
1461		q2 = TAILQ_NEXT(q1, link);
1462		free(q1);
1463		q1 = q2;
1464	}
1465
1466	/* List of tables */
1467	t1 = TAILQ_FIRST(&pft_table);
1468	while (t1 != NULL) {
1469		t2 = TAILQ_NEXT(t1, link);
1470		free(t1);
1471		t1 = t2;
1472	}
1473
1474	/* And the list of labeled filter rules */
1475	l1 = TAILQ_FIRST(&pfl_table);
1476	while (l1 != NULL) {
1477		l2 = TAILQ_NEXT(l1, link);
1478		free(l1);
1479		l1 = l2;
1480	}
1481
1482	close(dev);
1483	return (0);
1484}
1485
1486static void
1487pf_dump(void)
1488{
1489	pfi_refresh();
1490	if (altq_enabled) {
1491		pfq_refresh();
1492	}
1493	pft_refresh();
1494	pfl_refresh();
1495
1496	syslog(LOG_ERR, "Dump: pfi_table_age = %jd",
1497	    (intmax_t)pfi_table_age);
1498	syslog(LOG_ERR, "Dump: pfi_table_count = %d",
1499	    pfi_table_count);
1500
1501	syslog(LOG_ERR, "Dump: pfq_table_age = %jd",
1502	    (intmax_t)pfq_table_age);
1503	syslog(LOG_ERR, "Dump: pfq_table_count = %d",
1504	    pfq_table_count);
1505
1506	syslog(LOG_ERR, "Dump: pft_table_age = %jd",
1507	    (intmax_t)pft_table_age);
1508	syslog(LOG_ERR, "Dump: pft_table_count = %d",
1509	    pft_table_count);
1510
1511	syslog(LOG_ERR, "Dump: pfl_table_age = %jd",
1512	    (intmax_t)pfl_table_age);
1513	syslog(LOG_ERR, "Dump: pfl_table_count = %d",
1514	    pfl_table_count);
1515}
1516
1517const struct snmp_module config = {
1518	.comment = "This module implements a MIB for the pf packet filter.",
1519	.init =		pf_init,
1520	.fini =		pf_fini,
1521	.tree =		pf_ctree,
1522	.dump =		pf_dump,
1523	.tree_size =	pf_CTREE_SIZE,
1524};
1525