xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c (revision 78b9f0095b4af3aca6c931b2c7b009ddb8a05125)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2005 Philip Paeps <philip@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 #include <sys/queue.h>
32 #include <bsnmp/snmpmod.h>
33 
34 #include <net/pfvar.h>
35 #include <sys/ioctl.h>
36 
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdint.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <syslog.h>
44 #include <unistd.h>
45 
46 #define	SNMPTREE_TYPES
47 #include "pf_oid.h"
48 #include "pf_tree.h"
49 
50 struct lmodule *module;
51 
52 static int dev = -1;
53 static int started;
54 static uint64_t pf_tick;
55 
56 static struct pf_status pfs;
57 
58 enum { IN, OUT };
59 enum { IPV4, IPV6 };
60 enum { PASS, BLOCK };
61 
62 #define PFI_IFTYPE_GROUP	0
63 #define PFI_IFTYPE_INSTANCE	1
64 #define PFI_IFTYPE_DETACHED	2
65 
66 struct pfi_entry {
67 	struct pfi_kif	pfi;
68 	u_int		index;
69 	TAILQ_ENTRY(pfi_entry) link;
70 };
71 TAILQ_HEAD(pfi_table, pfi_entry);
72 
73 static struct pfi_table pfi_table;
74 static time_t pfi_table_age;
75 static int pfi_table_count;
76 
77 #define PFI_TABLE_MAXAGE	5
78 
79 struct pft_entry {
80 	struct pfr_tstats pft;
81 	u_int		index;
82 	TAILQ_ENTRY(pft_entry) link;
83 };
84 TAILQ_HEAD(pft_table, pft_entry);
85 
86 static struct pft_table pft_table;
87 static time_t pft_table_age;
88 static int pft_table_count;
89 
90 #define PFT_TABLE_MAXAGE	5
91 
92 struct pfa_entry {
93 	struct pfr_astats pfas;
94 	u_int		index;
95 	TAILQ_ENTRY(pfa_entry) link;
96 };
97 TAILQ_HEAD(pfa_table, pfa_entry);
98 
99 static struct pfa_table pfa_table;
100 static time_t pfa_table_age;
101 static int pfa_table_count;
102 
103 #define	PFA_TABLE_MAXAGE	5
104 
105 struct pfq_entry {
106 	struct pf_altq	altq;
107 	u_int		index;
108 	TAILQ_ENTRY(pfq_entry) link;
109 };
110 TAILQ_HEAD(pfq_table, pfq_entry);
111 
112 static struct pfq_table pfq_table;
113 static time_t pfq_table_age;
114 static int pfq_table_count;
115 
116 static int altq_enabled = 0;
117 
118 #define PFQ_TABLE_MAXAGE	5
119 
120 struct pfl_entry {
121 	char		name[MAXPATHLEN + PF_RULE_LABEL_SIZE];
122 	u_int64_t	evals;
123 	u_int64_t	bytes[2];
124 	u_int64_t	pkts[2];
125 	u_int		index;
126 	TAILQ_ENTRY(pfl_entry) link;
127 };
128 TAILQ_HEAD(pfl_table, pfl_entry);
129 
130 static struct pfl_table pfl_table;
131 static time_t pfl_table_age;
132 static int pfl_table_count;
133 
134 #define	PFL_TABLE_MAXAGE	5
135 
136 /* Forward declarations */
137 static int pfi_refresh(void);
138 static int pfq_refresh(void);
139 static int pfs_refresh(void);
140 static int pft_refresh(void);
141 static int pfa_refresh(void);
142 static int pfl_refresh(void);
143 static struct pfi_entry * pfi_table_find(u_int idx);
144 static struct pfq_entry * pfq_table_find(u_int idx);
145 static struct pft_entry * pft_table_find(u_int idx);
146 static struct pfa_entry * pfa_table_find(u_int idx);
147 static struct pfl_entry * pfl_table_find(u_int idx);
148 
149 static int altq_is_enabled(int pfdevice);
150 
151 int
152 pf_status(struct snmp_context __unused *ctx, struct snmp_value *val,
153 	u_int sub, u_int __unused vindex, enum snmp_op op)
154 {
155 	asn_subid_t	which = val->var.subs[sub - 1];
156 	time_t		runtime;
157 	unsigned char	str[128];
158 
159 	if (op == SNMP_OP_SET)
160 		return (SNMP_ERR_NOT_WRITEABLE);
161 
162 	if (op == SNMP_OP_GET) {
163 		if (pfs_refresh() == -1)
164 			return (SNMP_ERR_GENERR);
165 
166 		switch (which) {
167 			case LEAF_pfStatusRunning:
168 			    val->v.uint32 = pfs.running;
169 			    break;
170 			case LEAF_pfStatusRuntime:
171 			    runtime = (pfs.since > 0) ?
172 				time(NULL) - pfs.since : 0;
173 			    val->v.uint32 = runtime * 100;
174 			    break;
175 			case LEAF_pfStatusDebug:
176 			    val->v.uint32 = pfs.debug;
177 			    break;
178 			case LEAF_pfStatusHostId:
179 			    sprintf(str, "0x%08x", ntohl(pfs.hostid));
180 			    return (string_get(val, str, strlen(str)));
181 
182 			default:
183 			    return (SNMP_ERR_NOSUCHNAME);
184 		}
185 
186 		return (SNMP_ERR_NOERROR);
187 	}
188 
189 	abort();
190 }
191 
192 int
193 pf_counter(struct snmp_context __unused *ctx, struct snmp_value *val,
194 	u_int sub, u_int __unused vindex, enum snmp_op op)
195 {
196 	asn_subid_t	which = val->var.subs[sub - 1];
197 
198 	if (op == SNMP_OP_SET)
199 		return (SNMP_ERR_NOT_WRITEABLE);
200 
201 	if (op == SNMP_OP_GET) {
202 		if (pfs_refresh() == -1)
203 			return (SNMP_ERR_GENERR);
204 
205 		switch (which) {
206 			case LEAF_pfCounterMatch:
207 				val->v.counter64 = pfs.counters[PFRES_MATCH];
208 				break;
209 			case LEAF_pfCounterBadOffset:
210 				val->v.counter64 = pfs.counters[PFRES_BADOFF];
211 				break;
212 			case LEAF_pfCounterFragment:
213 				val->v.counter64 = pfs.counters[PFRES_FRAG];
214 				break;
215 			case LEAF_pfCounterShort:
216 				val->v.counter64 = pfs.counters[PFRES_SHORT];
217 				break;
218 			case LEAF_pfCounterNormalize:
219 				val->v.counter64 = pfs.counters[PFRES_NORM];
220 				break;
221 			case LEAF_pfCounterMemDrop:
222 				val->v.counter64 = pfs.counters[PFRES_MEMORY];
223 				break;
224 
225 			default:
226 				return (SNMP_ERR_NOSUCHNAME);
227 		}
228 
229 		return (SNMP_ERR_NOERROR);
230 	}
231 
232 	abort();
233 }
234 
235 int
236 pf_statetable(struct snmp_context __unused *ctx, struct snmp_value *val,
237 	u_int sub, u_int __unused vindex, enum snmp_op op)
238 {
239 	asn_subid_t	which = val->var.subs[sub - 1];
240 
241 	if (op == SNMP_OP_SET)
242 		return (SNMP_ERR_NOT_WRITEABLE);
243 
244 	if (op == SNMP_OP_GET) {
245 		if (pfs_refresh() == -1)
246 			return (SNMP_ERR_GENERR);
247 
248 		switch (which) {
249 			case LEAF_pfStateTableCount:
250 				val->v.uint32 = pfs.states;
251 				break;
252 			case LEAF_pfStateTableSearches:
253 				val->v.counter64 =
254 				    pfs.fcounters[FCNT_STATE_SEARCH];
255 				break;
256 			case LEAF_pfStateTableInserts:
257 				val->v.counter64 =
258 				    pfs.fcounters[FCNT_STATE_INSERT];
259 				break;
260 			case LEAF_pfStateTableRemovals:
261 				val->v.counter64 =
262 				    pfs.fcounters[FCNT_STATE_REMOVALS];
263 				break;
264 
265 			default:
266 				return (SNMP_ERR_NOSUCHNAME);
267 		}
268 
269 		return (SNMP_ERR_NOERROR);
270 	}
271 
272 	abort();
273 }
274 
275 int
276 pf_srcnodes(struct snmp_context __unused *ctx, struct snmp_value *val,
277 	u_int sub, u_int __unused vindex, enum snmp_op op)
278 {
279 	asn_subid_t	which = val->var.subs[sub - 1];
280 
281 	if (op == SNMP_OP_SET)
282 		return (SNMP_ERR_NOT_WRITEABLE);
283 
284 	if (op == SNMP_OP_GET) {
285 		if (pfs_refresh() == -1)
286 			return (SNMP_ERR_GENERR);
287 
288 		switch (which) {
289 			case LEAF_pfSrcNodesCount:
290 				val->v.uint32 = pfs.src_nodes;
291 				break;
292 			case LEAF_pfSrcNodesSearches:
293 				val->v.counter64 =
294 				    pfs.scounters[SCNT_SRC_NODE_SEARCH];
295 				break;
296 			case LEAF_pfSrcNodesInserts:
297 				val->v.counter64 =
298 				    pfs.scounters[SCNT_SRC_NODE_INSERT];
299 				break;
300 			case LEAF_pfSrcNodesRemovals:
301 				val->v.counter64 =
302 				    pfs.scounters[SCNT_SRC_NODE_REMOVALS];
303 				break;
304 
305 			default:
306 				return (SNMP_ERR_NOSUCHNAME);
307 		}
308 
309 		return (SNMP_ERR_NOERROR);
310 	}
311 
312 	abort();
313 }
314 
315 int
316 pf_limits(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_limit	pl;
321 
322 	if (op == SNMP_OP_SET)
323 		return (SNMP_ERR_NOT_WRITEABLE);
324 
325 	if (op == SNMP_OP_GET) {
326 		bzero(&pl, sizeof(struct pfioc_limit));
327 
328 		switch (which) {
329 			case LEAF_pfLimitsStates:
330 				pl.index = PF_LIMIT_STATES;
331 				break;
332 			case LEAF_pfLimitsSrcNodes:
333 				pl.index = PF_LIMIT_SRC_NODES;
334 				break;
335 			case LEAF_pfLimitsFrags:
336 				pl.index = PF_LIMIT_FRAGS;
337 				break;
338 
339 			default:
340 				return (SNMP_ERR_NOSUCHNAME);
341 		}
342 
343 		if (ioctl(dev, DIOCGETLIMIT, &pl)) {
344 			syslog(LOG_ERR, "pf_limits(): ioctl(): %s",
345 			    strerror(errno));
346 			return (SNMP_ERR_GENERR);
347 		}
348 
349 		val->v.uint32 = pl.limit;
350 
351 		return (SNMP_ERR_NOERROR);
352 	}
353 
354 	abort();
355 }
356 
357 int
358 pf_timeouts(struct snmp_context __unused *ctx, struct snmp_value *val,
359 	u_int sub, u_int __unused vindex, enum snmp_op op)
360 {
361 	asn_subid_t	which = val->var.subs[sub - 1];
362 	struct pfioc_tm	pt;
363 
364 	if (op == SNMP_OP_SET)
365 		return (SNMP_ERR_NOT_WRITEABLE);
366 
367 	if (op == SNMP_OP_GET) {
368 		bzero(&pt, sizeof(struct pfioc_tm));
369 
370 		switch (which) {
371 			case LEAF_pfTimeoutsTcpFirst:
372 				pt.timeout = PFTM_TCP_FIRST_PACKET;
373 				break;
374 			case LEAF_pfTimeoutsTcpOpening:
375 				pt.timeout = PFTM_TCP_OPENING;
376 				break;
377 			case LEAF_pfTimeoutsTcpEstablished:
378 				pt.timeout = PFTM_TCP_ESTABLISHED;
379 				break;
380 			case LEAF_pfTimeoutsTcpClosing:
381 				pt.timeout = PFTM_TCP_CLOSING;
382 				break;
383 			case LEAF_pfTimeoutsTcpFinWait:
384 				pt.timeout = PFTM_TCP_FIN_WAIT;
385 				break;
386 			case LEAF_pfTimeoutsTcpClosed:
387 				pt.timeout = PFTM_TCP_CLOSED;
388 				break;
389 			case LEAF_pfTimeoutsUdpFirst:
390 				pt.timeout = PFTM_UDP_FIRST_PACKET;
391 				break;
392 			case LEAF_pfTimeoutsUdpSingle:
393 				pt.timeout = PFTM_UDP_SINGLE;
394 				break;
395 			case LEAF_pfTimeoutsUdpMultiple:
396 				pt.timeout = PFTM_UDP_MULTIPLE;
397 				break;
398 			case LEAF_pfTimeoutsIcmpFirst:
399 				pt.timeout = PFTM_ICMP_FIRST_PACKET;
400 				break;
401 			case LEAF_pfTimeoutsIcmpError:
402 				pt.timeout = PFTM_ICMP_ERROR_REPLY;
403 				break;
404 			case LEAF_pfTimeoutsOtherFirst:
405 				pt.timeout = PFTM_OTHER_FIRST_PACKET;
406 				break;
407 			case LEAF_pfTimeoutsOtherSingle:
408 				pt.timeout = PFTM_OTHER_SINGLE;
409 				break;
410 			case LEAF_pfTimeoutsOtherMultiple:
411 				pt.timeout = PFTM_OTHER_MULTIPLE;
412 				break;
413 			case LEAF_pfTimeoutsFragment:
414 				pt.timeout = PFTM_FRAG;
415 				break;
416 			case LEAF_pfTimeoutsInterval:
417 				pt.timeout = PFTM_INTERVAL;
418 				break;
419 			case LEAF_pfTimeoutsAdaptiveStart:
420 				pt.timeout = PFTM_ADAPTIVE_START;
421 				break;
422 			case LEAF_pfTimeoutsAdaptiveEnd:
423 				pt.timeout = PFTM_ADAPTIVE_END;
424 				break;
425 			case LEAF_pfTimeoutsSrcNode:
426 				pt.timeout = PFTM_SRC_NODE;
427 				break;
428 
429 			default:
430 				return (SNMP_ERR_NOSUCHNAME);
431 		}
432 
433 		if (ioctl(dev, DIOCGETTIMEOUT, &pt)) {
434 			syslog(LOG_ERR, "pf_timeouts(): ioctl(): %s",
435 			    strerror(errno));
436 			return (SNMP_ERR_GENERR);
437 		}
438 
439 		val->v.integer = pt.seconds;
440 
441 		return (SNMP_ERR_NOERROR);
442 	}
443 
444 	abort();
445 }
446 
447 int
448 pf_logif(struct snmp_context __unused *ctx, struct snmp_value *val,
449 	u_int sub, u_int __unused vindex, enum snmp_op op)
450 {
451 	asn_subid_t	which = val->var.subs[sub - 1];
452 	unsigned char	str[IFNAMSIZ];
453 
454 	if (op == SNMP_OP_SET)
455 		return (SNMP_ERR_NOT_WRITEABLE);
456 
457 	if (op == SNMP_OP_GET) {
458 		if (pfs_refresh() == -1)
459 			return (SNMP_ERR_GENERR);
460 
461 		switch (which) {
462 	 		case LEAF_pfLogInterfaceName:
463 				strlcpy(str, pfs.ifname, sizeof str);
464 				return (string_get(val, str, strlen(str)));
465 			case LEAF_pfLogInterfaceIp4BytesIn:
466 				val->v.counter64 = pfs.bcounters[IPV4][IN];
467 				break;
468 			case LEAF_pfLogInterfaceIp4BytesOut:
469 				val->v.counter64 = pfs.bcounters[IPV4][OUT];
470 				break;
471 			case LEAF_pfLogInterfaceIp4PktsInPass:
472 				val->v.counter64 =
473 				    pfs.pcounters[IPV4][IN][PF_PASS];
474 				break;
475 			case LEAF_pfLogInterfaceIp4PktsInDrop:
476 				val->v.counter64 =
477 				    pfs.pcounters[IPV4][IN][PF_DROP];
478 				break;
479 			case LEAF_pfLogInterfaceIp4PktsOutPass:
480 				val->v.counter64 =
481 				    pfs.pcounters[IPV4][OUT][PF_PASS];
482 				break;
483 			case LEAF_pfLogInterfaceIp4PktsOutDrop:
484 				val->v.counter64 =
485 				    pfs.pcounters[IPV4][OUT][PF_DROP];
486 				break;
487 			case LEAF_pfLogInterfaceIp6BytesIn:
488 				val->v.counter64 = pfs.bcounters[IPV6][IN];
489 				break;
490 			case LEAF_pfLogInterfaceIp6BytesOut:
491 				val->v.counter64 = pfs.bcounters[IPV6][OUT];
492 				break;
493 			case LEAF_pfLogInterfaceIp6PktsInPass:
494 				val->v.counter64 =
495 				    pfs.pcounters[IPV6][IN][PF_PASS];
496 				break;
497 			case LEAF_pfLogInterfaceIp6PktsInDrop:
498 				val->v.counter64 =
499 				    pfs.pcounters[IPV6][IN][PF_DROP];
500 				break;
501 			case LEAF_pfLogInterfaceIp6PktsOutPass:
502 				val->v.counter64 =
503 				    pfs.pcounters[IPV6][OUT][PF_PASS];
504 				break;
505 			case LEAF_pfLogInterfaceIp6PktsOutDrop:
506 				val->v.counter64 =
507 				    pfs.pcounters[IPV6][OUT][PF_DROP];
508 				break;
509 
510 			default:
511 				return (SNMP_ERR_NOSUCHNAME);
512 		}
513 
514 		return (SNMP_ERR_NOERROR);
515 	}
516 
517 	abort();
518 }
519 
520 int
521 pf_interfaces(struct snmp_context __unused *ctx, struct snmp_value *val,
522 	u_int sub, u_int __unused vindex, enum snmp_op op)
523 {
524 	asn_subid_t	which = val->var.subs[sub - 1];
525 
526 	if (op == SNMP_OP_SET)
527 		return (SNMP_ERR_NOT_WRITEABLE);
528 
529 	if (op == SNMP_OP_GET) {
530 		if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
531 			if (pfi_refresh() == -1)
532 			    return (SNMP_ERR_GENERR);
533 
534 		switch (which) {
535 			case LEAF_pfInterfacesIfNumber:
536 				val->v.uint32 = pfi_table_count;
537 				break;
538 
539 			default:
540 				return (SNMP_ERR_NOSUCHNAME);
541 		}
542 
543 		return (SNMP_ERR_NOERROR);
544 	}
545 
546 	abort();
547 }
548 
549 int
550 pf_iftable(struct snmp_context __unused *ctx, struct snmp_value *val,
551 	u_int sub, u_int __unused vindex, enum snmp_op op)
552 {
553 	asn_subid_t	which = val->var.subs[sub - 1];
554 	struct pfi_entry *e = NULL;
555 
556 	if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
557 		pfi_refresh();
558 
559 	switch (op) {
560 		case SNMP_OP_SET:
561 			return (SNMP_ERR_NOT_WRITEABLE);
562 		case SNMP_OP_GETNEXT:
563 			if ((e = NEXT_OBJECT_INT(&pfi_table,
564 			    &val->var, sub)) == NULL)
565 				return (SNMP_ERR_NOSUCHNAME);
566 			val->var.len = sub + 1;
567 			val->var.subs[sub] = e->index;
568 			break;
569 		case SNMP_OP_GET:
570 			if (val->var.len - sub != 1)
571 				return (SNMP_ERR_NOSUCHNAME);
572 			if ((e = pfi_table_find(val->var.subs[sub])) == NULL)
573 				return (SNMP_ERR_NOSUCHNAME);
574 			break;
575 
576 		case SNMP_OP_COMMIT:
577 		case SNMP_OP_ROLLBACK:
578 		default:
579 			abort();
580 	}
581 
582 	switch (which) {
583 		case LEAF_pfInterfacesIfDescr:
584 			return (string_get(val, e->pfi.pfik_name, -1));
585 		case LEAF_pfInterfacesIfType:
586 			val->v.integer = PFI_IFTYPE_INSTANCE;
587 			break;
588 		case LEAF_pfInterfacesIfTZero:
589 			val->v.uint32 =
590 			    (time(NULL) - e->pfi.pfik_tzero) * 100;
591 			break;
592 		case LEAF_pfInterfacesIfRefsRule:
593 			val->v.uint32 = e->pfi.pfik_rulerefs;
594 			break;
595 		case LEAF_pfInterfacesIf4BytesInPass:
596 			val->v.counter64 =
597 			    e->pfi.pfik_bytes[IPV4][IN][PASS];
598 			break;
599 		case LEAF_pfInterfacesIf4BytesInBlock:
600 			val->v.counter64 =
601 			    e->pfi.pfik_bytes[IPV4][IN][BLOCK];
602 			break;
603 		case LEAF_pfInterfacesIf4BytesOutPass:
604 			val->v.counter64 =
605 			    e->pfi.pfik_bytes[IPV4][OUT][PASS];
606 			break;
607 		case LEAF_pfInterfacesIf4BytesOutBlock:
608 			val->v.counter64 =
609 			    e->pfi.pfik_bytes[IPV4][OUT][BLOCK];
610 			break;
611 		case LEAF_pfInterfacesIf4PktsInPass:
612 			val->v.counter64 =
613 			    e->pfi.pfik_packets[IPV4][IN][PASS];
614 			break;
615 		case LEAF_pfInterfacesIf4PktsInBlock:
616 			val->v.counter64 =
617 			    e->pfi.pfik_packets[IPV4][IN][BLOCK];
618 			break;
619 		case LEAF_pfInterfacesIf4PktsOutPass:
620 			val->v.counter64 =
621 			    e->pfi.pfik_packets[IPV4][OUT][PASS];
622 			break;
623 		case LEAF_pfInterfacesIf4PktsOutBlock:
624 			val->v.counter64 =
625 			    e->pfi.pfik_packets[IPV4][OUT][BLOCK];
626 			break;
627 		case LEAF_pfInterfacesIf6BytesInPass:
628 			val->v.counter64 =
629 			    e->pfi.pfik_bytes[IPV6][IN][PASS];
630 			break;
631 		case LEAF_pfInterfacesIf6BytesInBlock:
632 			val->v.counter64 =
633 			    e->pfi.pfik_bytes[IPV6][IN][BLOCK];
634 			break;
635 		case LEAF_pfInterfacesIf6BytesOutPass:
636 			val->v.counter64 =
637 			    e->pfi.pfik_bytes[IPV6][OUT][PASS];
638 			break;
639 		case LEAF_pfInterfacesIf6BytesOutBlock:
640 			val->v.counter64 =
641 			    e->pfi.pfik_bytes[IPV6][OUT][BLOCK];
642 			break;
643 		case LEAF_pfInterfacesIf6PktsInPass:
644 			val->v.counter64 =
645 			    e->pfi.pfik_packets[IPV6][IN][PASS];
646 			break;
647 		case LEAF_pfInterfacesIf6PktsInBlock:
648 			val->v.counter64 =
649 			    e->pfi.pfik_packets[IPV6][IN][BLOCK];
650 			break;
651 		case LEAF_pfInterfacesIf6PktsOutPass:
652 			val->v.counter64 =
653 			    e->pfi.pfik_packets[IPV6][OUT][PASS];
654 			break;
655 		case LEAF_pfInterfacesIf6PktsOutBlock:
656 			val->v.counter64 =
657 			    e->pfi.pfik_packets[IPV6][OUT][BLOCK];
658 			break;
659 
660 		default:
661 			return (SNMP_ERR_NOSUCHNAME);
662 	}
663 
664 	return (SNMP_ERR_NOERROR);
665 }
666 
667 int
668 pf_tables(struct snmp_context __unused *ctx, struct snmp_value *val,
669 	u_int sub, u_int __unused vindex, enum snmp_op op)
670 {
671 	asn_subid_t	which = val->var.subs[sub - 1];
672 
673 	if (op == SNMP_OP_SET)
674 		return (SNMP_ERR_NOT_WRITEABLE);
675 
676 	if (op == SNMP_OP_GET) {
677 		if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
678 			if (pft_refresh() == -1)
679 			    return (SNMP_ERR_GENERR);
680 
681 		switch (which) {
682 			case LEAF_pfTablesTblNumber:
683 				val->v.uint32 = pft_table_count;
684 				break;
685 
686 			default:
687 				return (SNMP_ERR_NOSUCHNAME);
688 		}
689 
690 		return (SNMP_ERR_NOERROR);
691 	}
692 
693 	abort();
694 }
695 
696 int
697 pf_tbltable(struct snmp_context __unused *ctx, struct snmp_value *val,
698 	u_int sub, u_int __unused vindex, enum snmp_op op)
699 {
700 	asn_subid_t	which = val->var.subs[sub - 1];
701 	struct pft_entry *e = NULL;
702 
703 	if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
704 		pft_refresh();
705 
706 	switch (op) {
707 		case SNMP_OP_SET:
708 			return (SNMP_ERR_NOT_WRITEABLE);
709 		case SNMP_OP_GETNEXT:
710 			if ((e = NEXT_OBJECT_INT(&pft_table,
711 			    &val->var, sub)) == NULL)
712 				return (SNMP_ERR_NOSUCHNAME);
713 			val->var.len = sub + 1;
714 			val->var.subs[sub] = e->index;
715 			break;
716 		case SNMP_OP_GET:
717 			if (val->var.len - sub != 1)
718 				return (SNMP_ERR_NOSUCHNAME);
719 			if ((e = pft_table_find(val->var.subs[sub])) == NULL)
720 				return (SNMP_ERR_NOSUCHNAME);
721 			break;
722 
723 		case SNMP_OP_COMMIT:
724 		case SNMP_OP_ROLLBACK:
725 		default:
726 			abort();
727 	}
728 
729 	switch (which) {
730 		case LEAF_pfTablesTblDescr:
731 			return (string_get(val, e->pft.pfrts_name, -1));
732 		case LEAF_pfTablesTblCount:
733 			val->v.integer = e->pft.pfrts_cnt;
734 			break;
735 		case LEAF_pfTablesTblTZero:
736 			val->v.uint32 =
737 			    (time(NULL) - e->pft.pfrts_tzero) * 100;
738 			break;
739 		case LEAF_pfTablesTblRefsAnchor:
740 			val->v.integer =
741 			    e->pft.pfrts_refcnt[PFR_REFCNT_ANCHOR];
742 			break;
743 		case LEAF_pfTablesTblRefsRule:
744 			val->v.integer =
745 			    e->pft.pfrts_refcnt[PFR_REFCNT_RULE];
746 			break;
747 		case LEAF_pfTablesTblEvalMatch:
748 			val->v.counter64 = e->pft.pfrts_match;
749 			break;
750 		case LEAF_pfTablesTblEvalNoMatch:
751 			val->v.counter64 = e->pft.pfrts_nomatch;
752 			break;
753 		case LEAF_pfTablesTblBytesInPass:
754 			val->v.counter64 =
755 			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_PASS];
756 			break;
757 		case LEAF_pfTablesTblBytesInBlock:
758 			val->v.counter64 =
759 			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_BLOCK];
760 			break;
761 		case LEAF_pfTablesTblBytesInXPass:
762 			val->v.counter64 =
763 			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_XPASS];
764 			break;
765 		case LEAF_pfTablesTblBytesOutPass:
766 			val->v.counter64 =
767 			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_PASS];
768 			break;
769 		case LEAF_pfTablesTblBytesOutBlock:
770 			val->v.counter64 =
771 			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_BLOCK];
772 			break;
773 		case LEAF_pfTablesTblBytesOutXPass:
774 			val->v.counter64 =
775 			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_XPASS];
776 			break;
777 		case LEAF_pfTablesTblPktsInPass:
778 			val->v.counter64 =
779 			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_PASS];
780 			break;
781 		case LEAF_pfTablesTblPktsInBlock:
782 			val->v.counter64 =
783 			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_BLOCK];
784 			break;
785 		case LEAF_pfTablesTblPktsInXPass:
786 			val->v.counter64 =
787 			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_XPASS];
788 			break;
789 		case LEAF_pfTablesTblPktsOutPass:
790 			val->v.counter64 =
791 			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_PASS];
792 			break;
793 		case LEAF_pfTablesTblPktsOutBlock:
794 			val->v.counter64 =
795 			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_BLOCK];
796 			break;
797 		case LEAF_pfTablesTblPktsOutXPass:
798 			val->v.counter64 =
799 			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_XPASS];
800 			break;
801 
802 		default:
803 			return (SNMP_ERR_NOSUCHNAME);
804 	}
805 
806 	return (SNMP_ERR_NOERROR);
807 }
808 
809 int
810 pf_tbladdr(struct snmp_context __unused *ctx, struct snmp_value __unused *val,
811 	u_int __unused sub, u_int __unused vindex, enum snmp_op __unused op)
812 {
813 	asn_subid_t	which = val->var.subs[sub - 1];
814 	struct pfa_entry *e = NULL;
815 
816 	if ((time(NULL) - pfa_table_age) > PFA_TABLE_MAXAGE)
817 		pfa_refresh();
818 
819 	switch (op) {
820 		case SNMP_OP_SET:
821 			return (SNMP_ERR_NOT_WRITEABLE);
822 		case SNMP_OP_GETNEXT:
823 			if ((e = NEXT_OBJECT_INT(&pfa_table,
824 			    &val->var, sub)) == NULL)
825 				return (SNMP_ERR_NOSUCHNAME);
826 			val->var.len = sub + 1;
827 			val->var.subs[sub] = e->index;
828 			break;
829 		case SNMP_OP_GET:
830 			if (val->var.len - sub != 1)
831 				return (SNMP_ERR_NOSUCHNAME);
832 			if ((e = pfa_table_find(val->var.subs[sub])) == NULL)
833 				return (SNMP_ERR_NOSUCHNAME);
834 			break;
835 
836 		case SNMP_OP_COMMIT:
837 		case SNMP_OP_ROLLBACK:
838 		default:
839 			abort();
840 	}
841 
842 	switch (which) {
843 		case LEAF_pfTablesAddrNetType:
844 			if (e->pfas.pfras_a.pfra_af == AF_INET)
845 				val->v.integer = pfTablesAddrNetType_ipv4;
846 			else if (e->pfas.pfras_a.pfra_af == AF_INET6)
847 				val->v.integer = pfTablesAddrNetType_ipv6;
848 			else
849 				return (SNMP_ERR_GENERR);
850 			break;
851 		case LEAF_pfTablesAddrNet:
852 			if (e->pfas.pfras_a.pfra_af == AF_INET) {
853 				return (string_get(val,
854 				    (u_char *)&e->pfas.pfras_a.pfra_ip4addr, 4));
855 			} else if (e->pfas.pfras_a.pfra_af == AF_INET6)
856 				return (string_get(val,
857 				    (u_char *)&e->pfas.pfras_a.pfra_ip6addr, 16));
858 			else
859 				return (SNMP_ERR_GENERR);
860 			break;
861 		case LEAF_pfTablesAddrPrefix:
862 			val->v.integer = (int32_t) e->pfas.pfras_a.pfra_net;
863 			break;
864 		case LEAF_pfTablesAddrTZero:
865 			val->v.uint32 =
866 			    (time(NULL) - e->pfas.pfras_tzero) * 100;
867 			break;
868 		case LEAF_pfTablesAddrBytesInPass:
869 			val->v.counter64 =
870 			    e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_PASS];
871 			break;
872 		case LEAF_pfTablesAddrBytesInBlock:
873 			val->v.counter64 =
874 			    e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_BLOCK];
875 			break;
876 		case LEAF_pfTablesAddrBytesOutPass:
877 			val->v.counter64 =
878 			    e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_PASS];
879 			break;
880 		case LEAF_pfTablesAddrBytesOutBlock:
881 			val->v.counter64 =
882 			    e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_BLOCK];
883 			break;
884 		case LEAF_pfTablesAddrPktsInPass:
885 			val->v.counter64 =
886 			    e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_PASS];
887 			break;
888 		case LEAF_pfTablesAddrPktsInBlock:
889 			val->v.counter64 =
890 			    e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_BLOCK];
891 			break;
892 		case LEAF_pfTablesAddrPktsOutPass:
893 			val->v.counter64 =
894 			    e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_PASS];
895 			break;
896 		case LEAF_pfTablesAddrPktsOutBlock:
897 			val->v.counter64 =
898 			    e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_BLOCK];
899 			break;
900 		default:
901 			return (SNMP_ERR_NOSUCHNAME);
902 	}
903 
904 	return (SNMP_ERR_NOERROR);
905 }
906 
907 int
908 pf_altq(struct snmp_context __unused *ctx, struct snmp_value *val,
909 	u_int sub, u_int __unused vindex, enum snmp_op op)
910 {
911 	asn_subid_t	which = val->var.subs[sub - 1];
912 
913 	if (!altq_enabled)
914 	   return (SNMP_ERR_NOSUCHNAME);
915 
916 	if (op == SNMP_OP_SET)
917 		return (SNMP_ERR_NOT_WRITEABLE);
918 
919 	if (op == SNMP_OP_GET) {
920 		if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
921 			if (pfq_refresh() == -1)
922 			    return (SNMP_ERR_GENERR);
923 
924 		switch (which) {
925 			case LEAF_pfAltqQueueNumber:
926 				val->v.uint32 = pfq_table_count;
927 				break;
928 
929 			default:
930 				return (SNMP_ERR_NOSUCHNAME);
931 		}
932 
933 		return (SNMP_ERR_NOERROR);
934 	}
935 
936 	abort();
937 	return (SNMP_ERR_GENERR);
938 }
939 
940 int
941 pf_altqq(struct snmp_context __unused *ctx, struct snmp_value *val,
942 	u_int sub, u_int __unused vindex, enum snmp_op op)
943 {
944 	asn_subid_t	which = val->var.subs[sub - 1];
945 	struct pfq_entry *e = NULL;
946 
947 	if (!altq_enabled)
948 	   return (SNMP_ERR_NOSUCHNAME);
949 
950 	if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
951 		pfq_refresh();
952 
953 	switch (op) {
954 		case SNMP_OP_SET:
955 			return (SNMP_ERR_NOT_WRITEABLE);
956 		case SNMP_OP_GETNEXT:
957 			if ((e = NEXT_OBJECT_INT(&pfq_table,
958 			    &val->var, sub)) == NULL)
959 				return (SNMP_ERR_NOSUCHNAME);
960 			val->var.len = sub + 1;
961 			val->var.subs[sub] = e->index;
962 			break;
963 		case SNMP_OP_GET:
964 			if (val->var.len - sub != 1)
965 				return (SNMP_ERR_NOSUCHNAME);
966 			if ((e = pfq_table_find(val->var.subs[sub])) == NULL)
967 				return (SNMP_ERR_NOSUCHNAME);
968 			break;
969 
970 		case SNMP_OP_COMMIT:
971 		case SNMP_OP_ROLLBACK:
972 		default:
973 			abort();
974 	}
975 
976 	switch (which) {
977 		case LEAF_pfAltqQueueDescr:
978 			return (string_get(val, e->altq.qname, -1));
979 		case LEAF_pfAltqQueueParent:
980 			return (string_get(val, e->altq.parent, -1));
981 		case LEAF_pfAltqQueueScheduler:
982 			val->v.integer = e->altq.scheduler;
983 			break;
984 		case LEAF_pfAltqQueueBandwidth:
985 			val->v.uint32 = e->altq.bandwidth;
986 			break;
987 		case LEAF_pfAltqQueuePriority:
988 			val->v.integer = e->altq.priority;
989 			break;
990 		case LEAF_pfAltqQueueLimit:
991 			val->v.integer = e->altq.qlimit;
992 			break;
993 
994 		default:
995 			return (SNMP_ERR_NOSUCHNAME);
996 	}
997 
998 	return (SNMP_ERR_NOERROR);
999 }
1000 
1001 int
1002 pf_labels(struct snmp_context __unused *ctx, struct snmp_value *val,
1003 	u_int sub, u_int __unused vindex, enum snmp_op op)
1004 {
1005 	asn_subid_t	which = val->var.subs[sub - 1];
1006 
1007 	if (op == SNMP_OP_SET)
1008 		return (SNMP_ERR_NOT_WRITEABLE);
1009 
1010 	if (op == SNMP_OP_GET) {
1011 		if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE)
1012 			if (pfl_refresh() == -1)
1013 				return (SNMP_ERR_GENERR);
1014 
1015 		switch (which) {
1016 			case LEAF_pfLabelsLblNumber:
1017 				val->v.uint32 = pfl_table_count;
1018 				break;
1019 
1020 			default:
1021 				return (SNMP_ERR_NOSUCHNAME);
1022 		}
1023 
1024 		return (SNMP_ERR_NOERROR);
1025 	}
1026 
1027 	abort();
1028 	return (SNMP_ERR_GENERR);
1029 }
1030 
1031 int
1032 pf_lbltable(struct snmp_context __unused *ctx, struct snmp_value *val,
1033 	u_int sub, u_int __unused vindex, enum snmp_op op)
1034 {
1035 	asn_subid_t	which = val->var.subs[sub - 1];
1036 	struct pfl_entry *e = NULL;
1037 
1038 	if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE)
1039 		pfl_refresh();
1040 
1041 	switch (op) {
1042 		case SNMP_OP_SET:
1043 			return (SNMP_ERR_NOT_WRITEABLE);
1044 		case SNMP_OP_GETNEXT:
1045 			if ((e = NEXT_OBJECT_INT(&pfl_table,
1046 			    &val->var, sub)) == NULL)
1047 				return (SNMP_ERR_NOSUCHNAME);
1048 			val->var.len = sub + 1;
1049 			val->var.subs[sub] = e->index;
1050 			break;
1051 		case SNMP_OP_GET:
1052 			if (val->var.len - sub != 1)
1053 				return (SNMP_ERR_NOSUCHNAME);
1054 			if ((e = pfl_table_find(val->var.subs[sub])) == NULL)
1055 				return (SNMP_ERR_NOSUCHNAME);
1056 			break;
1057 
1058 		case SNMP_OP_COMMIT:
1059 		case SNMP_OP_ROLLBACK:
1060 		default:
1061 			abort();
1062 	}
1063 
1064 	switch (which) {
1065 		case LEAF_pfLabelsLblName:
1066 			return (string_get(val, e->name, -1));
1067 		case LEAF_pfLabelsLblEvals:
1068 			val->v.counter64 = e->evals;
1069 			break;
1070 		case LEAF_pfLabelsLblBytesIn:
1071 			val->v.counter64 = e->bytes[IN];
1072 			break;
1073 		case LEAF_pfLabelsLblBytesOut:
1074 			val->v.counter64 = e->bytes[OUT];
1075 			break;
1076 		case LEAF_pfLabelsLblPktsIn:
1077 			val->v.counter64 = e->pkts[IN];
1078 			break;
1079 		case LEAF_pfLabelsLblPktsOut:
1080 			val->v.counter64 = e->pkts[OUT];
1081 			break;
1082 		default:
1083 			return (SNMP_ERR_NOSUCHNAME);
1084 	}
1085 
1086 	return (SNMP_ERR_NOERROR);
1087 }
1088 
1089 static struct pfi_entry *
1090 pfi_table_find(u_int idx)
1091 {
1092 	struct pfi_entry *e;
1093 
1094 	TAILQ_FOREACH(e, &pfi_table, link)
1095 		if (e->index == idx)
1096 			return (e);
1097 	return (NULL);
1098 }
1099 
1100 static struct pfq_entry *
1101 pfq_table_find(u_int idx)
1102 {
1103 	struct pfq_entry *e;
1104 
1105 	TAILQ_FOREACH(e, &pfq_table, link)
1106 		if (e->index == idx)
1107 			return (e);
1108 	return (NULL);
1109 }
1110 
1111 static struct pft_entry *
1112 pft_table_find(u_int idx)
1113 {
1114 	struct pft_entry *e;
1115 
1116 	TAILQ_FOREACH(e, &pft_table, link)
1117 		if (e->index == idx)
1118 			return (e);
1119 	return (NULL);
1120 }
1121 
1122 static struct pfa_entry *
1123 pfa_table_find(u_int idx)
1124 {
1125 	struct pfa_entry *e;
1126 
1127 	TAILQ_FOREACH(e, &pfa_table, link)
1128 		if (e->index == idx)
1129 			return (e);
1130 	return (NULL);
1131 }
1132 
1133 static struct pfl_entry *
1134 pfl_table_find(u_int idx)
1135 {
1136 	struct pfl_entry *e;
1137 
1138 	TAILQ_FOREACH(e, &pfl_table, link)
1139 		if (e->index == idx)
1140 			return (e);
1141 
1142 	return (NULL);
1143 }
1144 
1145 static int
1146 pfi_refresh(void)
1147 {
1148 	struct pfioc_iface io;
1149 	struct pfi_kif *p = NULL;
1150 	struct pfi_entry *e;
1151 	int i, numifs = 1;
1152 
1153 	if (started && this_tick <= pf_tick)
1154 		return (0);
1155 
1156 	while (!TAILQ_EMPTY(&pfi_table)) {
1157 		e = TAILQ_FIRST(&pfi_table);
1158 		TAILQ_REMOVE(&pfi_table, e, link);
1159 		free(e);
1160 	}
1161 
1162 	bzero(&io, sizeof(io));
1163 	io.pfiio_esize = sizeof(struct pfi_kif);
1164 
1165 	for (;;) {
1166 		p = reallocf(p, numifs * sizeof(struct pfi_kif));
1167 		if (p == NULL) {
1168 			syslog(LOG_ERR, "pfi_refresh(): reallocf() numifs=%d: %s",
1169 			    numifs, strerror(errno));
1170 			goto err2;
1171 		}
1172 		io.pfiio_size = numifs;
1173 		io.pfiio_buffer = p;
1174 
1175 		if (ioctl(dev, DIOCIGETIFACES, &io)) {
1176 			syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s",
1177 			    strerror(errno));
1178 			goto err2;
1179 		}
1180 
1181 		if (numifs >= io.pfiio_size)
1182 			break;
1183 
1184 		numifs = io.pfiio_size;
1185 	}
1186 
1187 	for (i = 0; i < numifs; i++) {
1188 		e = malloc(sizeof(struct pfi_entry));
1189 		if (e == NULL)
1190 			goto err1;
1191 		e->index = i + 1;
1192 		memcpy(&e->pfi, p+i, sizeof(struct pfi_kif));
1193 		TAILQ_INSERT_TAIL(&pfi_table, e, link);
1194 	}
1195 
1196 	pfi_table_age = time(NULL);
1197 	pfi_table_count = numifs;
1198 	pf_tick = this_tick;
1199 
1200 	free(p);
1201 	return (0);
1202 
1203 err1:
1204 	while (!TAILQ_EMPTY(&pfi_table)) {
1205 		e = TAILQ_FIRST(&pfi_table);
1206 		TAILQ_REMOVE(&pfi_table, e, link);
1207 		free(e);
1208 	}
1209 err2:
1210 	free(p);
1211 	return(-1);
1212 }
1213 
1214 static int
1215 pfq_refresh(void)
1216 {
1217 	struct pfioc_altq pa;
1218 	struct pfq_entry *e;
1219 	int i, numqs, ticket;
1220 
1221 	if (started && this_tick <= pf_tick)
1222 		return (0);
1223 
1224 	while (!TAILQ_EMPTY(&pfq_table)) {
1225 		e = TAILQ_FIRST(&pfq_table);
1226 		TAILQ_REMOVE(&pfq_table, e, link);
1227 		free(e);
1228 	}
1229 
1230 	bzero(&pa, sizeof(pa));
1231 
1232 	if (ioctl(dev, DIOCGETALTQS, &pa)) {
1233 		syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s",
1234 		    strerror(errno));
1235 		return (-1);
1236 	}
1237 
1238 	numqs = pa.nr;
1239 	ticket = pa.ticket;
1240 
1241 	for (i = 0; i < numqs; i++) {
1242 		e = malloc(sizeof(struct pfq_entry));
1243 		if (e == NULL) {
1244 			syslog(LOG_ERR, "pfq_refresh(): "
1245 			    "malloc(): %s",
1246 			    strerror(errno));
1247 			goto err;
1248 		}
1249 		pa.ticket = ticket;
1250 		pa.nr = i;
1251 
1252 		if (ioctl(dev, DIOCGETALTQ, &pa)) {
1253 			syslog(LOG_ERR, "pfq_refresh(): "
1254 			    "ioctl(DIOCGETALTQ): %s",
1255 			    strerror(errno));
1256 			goto err;
1257 		}
1258 
1259 		if (pa.altq.qid > 0) {
1260 			memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq));
1261 			e->index = pa.altq.qid;
1262 			pfq_table_count = i;
1263 			INSERT_OBJECT_INT_LINK_INDEX(e, &pfq_table, link, index);
1264 		}
1265 	}
1266 
1267 	pfq_table_age = time(NULL);
1268 	pf_tick = this_tick;
1269 
1270 	return (0);
1271 err:
1272 	free(e);
1273 	while (!TAILQ_EMPTY(&pfq_table)) {
1274 		e = TAILQ_FIRST(&pfq_table);
1275 		TAILQ_REMOVE(&pfq_table, e, link);
1276 		free(e);
1277 	}
1278 	return(-1);
1279 }
1280 
1281 static int
1282 pfs_refresh(void)
1283 {
1284 	if (started && this_tick <= pf_tick)
1285 		return (0);
1286 
1287 	bzero(&pfs, sizeof(struct pf_status));
1288 
1289 	if (ioctl(dev, DIOCGETSTATUS, &pfs)) {
1290 		syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s",
1291 		    strerror(errno));
1292 		return (-1);
1293 	}
1294 
1295 	pf_tick = this_tick;
1296 	return (0);
1297 }
1298 
1299 static int
1300 pft_refresh(void)
1301 {
1302 	struct pfioc_table io;
1303 	struct pfr_tstats *t = NULL;
1304 	struct pft_entry *e;
1305 	int i, numtbls = 1;
1306 
1307 	if (started && this_tick <= pf_tick)
1308 		return (0);
1309 
1310 	while (!TAILQ_EMPTY(&pft_table)) {
1311 		e = TAILQ_FIRST(&pft_table);
1312 		TAILQ_REMOVE(&pft_table, e, link);
1313 		free(e);
1314 	}
1315 
1316 	bzero(&io, sizeof(io));
1317 	io.pfrio_esize = sizeof(struct pfr_tstats);
1318 
1319 	for (;;) {
1320 		t = reallocf(t, numtbls * sizeof(struct pfr_tstats));
1321 		if (t == NULL) {
1322 			syslog(LOG_ERR, "pft_refresh(): reallocf() numtbls=%d: %s",
1323 			    numtbls, strerror(errno));
1324 			goto err2;
1325 		}
1326 		io.pfrio_size = numtbls;
1327 		io.pfrio_buffer = t;
1328 
1329 		if (ioctl(dev, DIOCRGETTSTATS, &io)) {
1330 			syslog(LOG_ERR, "pft_refresh(): ioctl(): %s",
1331 			    strerror(errno));
1332 			goto err2;
1333 		}
1334 
1335 		if (numtbls >= io.pfrio_size)
1336 			break;
1337 
1338 		numtbls = io.pfrio_size;
1339 	}
1340 
1341 	for (i = 0; i < numtbls; i++) {
1342 		e = malloc(sizeof(struct pft_entry));
1343 		if (e == NULL)
1344 			goto err1;
1345 		e->index = i + 1;
1346 		memcpy(&e->pft, t+i, sizeof(struct pfr_tstats));
1347 		TAILQ_INSERT_TAIL(&pft_table, e, link);
1348 	}
1349 
1350 	pft_table_age = time(NULL);
1351 	pft_table_count = numtbls;
1352 	pf_tick = this_tick;
1353 
1354 	free(t);
1355 	return (0);
1356 err1:
1357 	while (!TAILQ_EMPTY(&pft_table)) {
1358 		e = TAILQ_FIRST(&pft_table);
1359 		TAILQ_REMOVE(&pft_table, e, link);
1360 		free(e);
1361 	}
1362 err2:
1363 	free(t);
1364 	return(-1);
1365 }
1366 
1367 static int
1368 pfa_table_addrs(u_int sidx, struct pfr_table *pt)
1369 {
1370 	struct pfioc_table io;
1371 	struct pfr_astats *t = NULL;
1372 	struct pfa_entry *e;
1373 	int i, numaddrs = 1;
1374 
1375 	if (pt == NULL)
1376 		return (-1);
1377 
1378 	memset(&io, 0, sizeof(io));
1379 	strlcpy(io.pfrio_table.pfrt_name, pt->pfrt_name,
1380 	    sizeof(io.pfrio_table.pfrt_name));
1381 
1382 	for (;;) {
1383 		t = reallocf(t, numaddrs * sizeof(struct pfr_astats));
1384 		if (t == NULL) {
1385 			syslog(LOG_ERR, "pfa_table_addrs(): reallocf(): %s",
1386 			    strerror(errno));
1387 			numaddrs = -1;
1388 			goto error;
1389 		}
1390 
1391 		memset(t, 0, sizeof(*t));
1392 		io.pfrio_size = numaddrs;
1393 		io.pfrio_buffer = t;
1394 		io.pfrio_esize = sizeof(struct pfr_astats);
1395 
1396 		if (ioctl(dev, DIOCRGETASTATS, &io)) {
1397 			syslog(LOG_ERR, "pfa_table_addrs(): ioctl() on %s: %s",
1398 			    pt->pfrt_name, strerror(errno));
1399 			numaddrs = -1;
1400 			break;
1401 		}
1402 
1403 		if (numaddrs >= io.pfrio_size)
1404 			break;
1405 
1406 		numaddrs = io.pfrio_size;
1407 	}
1408 
1409 	for (i = 0; i < numaddrs; i++) {
1410 		if ((t + i)->pfras_a.pfra_af != AF_INET &&
1411 		    (t + i)->pfras_a.pfra_af != AF_INET6) {
1412 			numaddrs = i;
1413 			break;
1414 		}
1415 
1416 		e = (struct pfa_entry *)malloc(sizeof(struct pfa_entry));
1417 		if (e == NULL) {
1418 			syslog(LOG_ERR, "pfa_table_addrs(): malloc(): %s",
1419 			    strerror(errno));
1420 			numaddrs = -1;
1421 			break;
1422 		}
1423 		e->index = sidx + i;
1424 		memcpy(&e->pfas, t + i, sizeof(struct pfr_astats));
1425 		TAILQ_INSERT_TAIL(&pfa_table, e, link);
1426 	}
1427 
1428 	free(t);
1429 error:
1430 	return (numaddrs);
1431 }
1432 
1433 static int
1434 pfa_refresh(void)
1435 {
1436 	struct pfioc_table io;
1437 	struct pfr_table *pt = NULL, *it = NULL;
1438 	struct pfa_entry *e;
1439 	int i, numtbls = 1, cidx, naddrs;
1440 
1441 	if (started && this_tick <= pf_tick)
1442 		return (0);
1443 
1444 	while (!TAILQ_EMPTY(&pfa_table)) {
1445 		e = TAILQ_FIRST(&pfa_table);
1446 		TAILQ_REMOVE(&pfa_table, e, link);
1447 		free(e);
1448 	}
1449 
1450 	memset(&io, 0, sizeof(io));
1451 	io.pfrio_esize = sizeof(struct pfr_table);
1452 
1453 	for (;;) {
1454 		pt = reallocf(pt, numtbls * sizeof(struct pfr_table));
1455 		if (pt == NULL) {
1456 			syslog(LOG_ERR, "pfa_refresh(): reallocf() %s",
1457 			    strerror(errno));
1458 			return (-1);
1459 		}
1460 		memset(pt, 0, sizeof(*pt));
1461 		io.pfrio_size = numtbls;
1462 		io.pfrio_buffer = pt;
1463 
1464 		if (ioctl(dev, DIOCRGETTABLES, &io)) {
1465 			syslog(LOG_ERR, "pfa_refresh(): ioctl(): %s",
1466 			    strerror(errno));
1467 			goto err2;
1468 		}
1469 
1470 		if (numtbls >= io.pfrio_size)
1471 			break;
1472 
1473 		numtbls = io.pfrio_size;
1474 	}
1475 
1476 	cidx = 1;
1477 
1478 	for (it = pt, i = 0; i < numtbls; it++, i++) {
1479 		/*
1480 		 * Skip the table if not active - ioctl(DIOCRGETASTATS) will
1481 		 * return ESRCH for this entry anyway.
1482 		 */
1483 		if (!(it->pfrt_flags & PFR_TFLAG_ACTIVE))
1484 			continue;
1485 
1486 		if ((naddrs = pfa_table_addrs(cidx, it)) < 0)
1487 			goto err1;
1488 
1489 		cidx += naddrs;
1490 	}
1491 
1492 	pfa_table_age = time(NULL);
1493 	pfa_table_count = cidx;
1494 	pf_tick = this_tick;
1495 
1496 	free(pt);
1497 	return (0);
1498 err1:
1499 	while (!TAILQ_EMPTY(&pfa_table)) {
1500 		e = TAILQ_FIRST(&pfa_table);
1501 		TAILQ_REMOVE(&pfa_table, e, link);
1502 		free(e);
1503 	}
1504 
1505 err2:
1506 	free(pt);
1507 	return (-1);
1508 }
1509 
1510 static int
1511 pfl_scan_ruleset(const char *path)
1512 {
1513 	struct pfioc_rule pr;
1514 	struct pfl_entry *e;
1515 	u_int32_t nr, i;
1516 
1517 	bzero(&pr, sizeof(pr));
1518 	strlcpy(pr.anchor, path, sizeof(pr.anchor));
1519 	pr.rule.action = PF_PASS;
1520 	if (ioctl(dev, DIOCGETRULES, &pr)) {
1521 		syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULES): %s",
1522 		    strerror(errno));
1523 		goto err;
1524 	}
1525 
1526 	for (nr = pr.nr, i = 0; i < nr; i++) {
1527 		pr.nr = i;
1528 		if (ioctl(dev, DIOCGETRULE, &pr)) {
1529 			syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULE):"
1530 			    " %s", strerror(errno));
1531 			goto err;
1532 		}
1533 
1534 		if (pr.rule.label[0]) {
1535 			e = (struct pfl_entry *)malloc(sizeof(*e));
1536 			if (e == NULL)
1537 				goto err;
1538 
1539 			strlcpy(e->name, path, sizeof(e->name));
1540 			if (path[0])
1541 				strlcat(e->name, "/", sizeof(e->name));
1542 			strlcat(e->name, pr.rule.label, sizeof(e->name));
1543 
1544 			e->evals = pr.rule.evaluations;
1545 			e->bytes[IN] = pr.rule.bytes[IN];
1546 			e->bytes[OUT] = pr.rule.bytes[OUT];
1547 			e->pkts[IN] = pr.rule.packets[IN];
1548 			e->pkts[OUT] = pr.rule.packets[OUT];
1549 			e->index = ++pfl_table_count;
1550 
1551 			TAILQ_INSERT_TAIL(&pfl_table, e, link);
1552 		}
1553 	}
1554 
1555 	return (0);
1556 
1557 err:
1558 	return (-1);
1559 }
1560 
1561 static int
1562 pfl_walk_rulesets(const char *path)
1563 {
1564 	struct pfioc_ruleset prs;
1565 	char newpath[MAXPATHLEN];
1566 	u_int32_t nr, i;
1567 
1568 	if (pfl_scan_ruleset(path))
1569 		goto err;
1570 
1571 	bzero(&prs, sizeof(prs));
1572 	strlcpy(prs.path, path, sizeof(prs.path));
1573 	if (ioctl(dev, DIOCGETRULESETS, &prs)) {
1574 		syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESETS): %s",
1575 		    strerror(errno));
1576 		goto err;
1577 	}
1578 
1579 	for (nr = prs.nr, i = 0; i < nr; i++) {
1580 		prs.nr = i;
1581 		if (ioctl(dev, DIOCGETRULESET, &prs)) {
1582 			syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESET):"
1583 			    " %s", strerror(errno));
1584 			goto err;
1585 		}
1586 
1587 		if (strcmp(prs.name, PF_RESERVED_ANCHOR) == 0)
1588 			continue;
1589 
1590 		strlcpy(newpath, path, sizeof(newpath));
1591 		if (path[0])
1592 			strlcat(newpath, "/", sizeof(newpath));
1593 
1594 		strlcat(newpath, prs.name, sizeof(newpath));
1595 		if (pfl_walk_rulesets(newpath))
1596 			goto err;
1597 	}
1598 
1599 	return (0);
1600 
1601 err:
1602 	return (-1);
1603 }
1604 
1605 static int
1606 pfl_refresh(void)
1607 {
1608 	struct pfl_entry *e;
1609 
1610 	if (started && this_tick <= pf_tick)
1611 		return (0);
1612 
1613 	while (!TAILQ_EMPTY(&pfl_table)) {
1614 		e = TAILQ_FIRST(&pfl_table);
1615 		TAILQ_REMOVE(&pfl_table, e, link);
1616 		free(e);
1617 	}
1618 	pfl_table_count = 0;
1619 
1620 	if (pfl_walk_rulesets(""))
1621 		goto err;
1622 
1623 	pfl_table_age = time(NULL);
1624 	pf_tick = this_tick;
1625 
1626 	return (0);
1627 
1628 err:
1629 	while (!TAILQ_EMPTY(&pfl_table)) {
1630 		e = TAILQ_FIRST(&pfl_table);
1631 		TAILQ_REMOVE(&pfl_table, e, link);
1632 		free(e);
1633 	}
1634 	pfl_table_count = 0;
1635 
1636 	return (-1);
1637 }
1638 
1639 /*
1640  * check whether altq support is enabled in kernel
1641  */
1642 
1643 static int
1644 altq_is_enabled(int pfdev)
1645 {
1646 	struct pfioc_altq pa;
1647 
1648 	errno = 0;
1649 	if (ioctl(pfdev, DIOCGETALTQS, &pa)) {
1650 		if (errno == ENODEV) {
1651 			syslog(LOG_INFO, "No ALTQ support in kernel\n"
1652 			    "ALTQ related functions disabled\n");
1653 			return (0);
1654 		} else
1655 			syslog(LOG_ERR, "DIOCGETALTQS returned an error: %s",
1656 			    strerror(errno));
1657 			return (-1);
1658 	}
1659 	return (1);
1660 }
1661 
1662 /*
1663  * Implement the bsnmpd module interface
1664  */
1665 static int
1666 pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[])
1667 {
1668 	module = mod;
1669 
1670 	if ((dev = open("/dev/pf", O_RDONLY)) == -1) {
1671 		syslog(LOG_ERR, "pf_init(): open(): %s\n",
1672 		    strerror(errno));
1673 		return (-1);
1674 	}
1675 
1676 	if ((altq_enabled = altq_is_enabled(dev)) == -1) {
1677 		syslog(LOG_ERR, "pf_init(): altq test failed");
1678 		return (-1);
1679 	}
1680 
1681 	/* Prepare internal state */
1682 	TAILQ_INIT(&pfi_table);
1683 	TAILQ_INIT(&pfq_table);
1684 	TAILQ_INIT(&pft_table);
1685 	TAILQ_INIT(&pfa_table);
1686 	TAILQ_INIT(&pfl_table);
1687 
1688 	pfi_refresh();
1689 	if (altq_enabled) {
1690 		pfq_refresh();
1691 	}
1692 
1693 	pfs_refresh();
1694 	pft_refresh();
1695 	pfa_refresh();
1696 	pfl_refresh();
1697 
1698 	started = 1;
1699 
1700 	return (0);
1701 }
1702 
1703 static int
1704 pf_fini(void)
1705 {
1706 	struct pfi_entry *i1, *i2;
1707 	struct pfq_entry *q1, *q2;
1708 	struct pft_entry *t1, *t2;
1709 	struct pfa_entry *a1, *a2;
1710 	struct pfl_entry *l1, *l2;
1711 
1712 	/* Empty the list of interfaces */
1713 	i1 = TAILQ_FIRST(&pfi_table);
1714 	while (i1 != NULL) {
1715 		i2 = TAILQ_NEXT(i1, link);
1716 		free(i1);
1717 		i1 = i2;
1718 	}
1719 
1720 	/* List of queues */
1721 	q1 = TAILQ_FIRST(&pfq_table);
1722 	while (q1 != NULL) {
1723 		q2 = TAILQ_NEXT(q1, link);
1724 		free(q1);
1725 		q1 = q2;
1726 	}
1727 
1728 	/* List of tables */
1729 	t1 = TAILQ_FIRST(&pft_table);
1730 	while (t1 != NULL) {
1731 		t2 = TAILQ_NEXT(t1, link);
1732 		free(t1);
1733 		t1 = t2;
1734 	}
1735 
1736 	/* List of table addresses */
1737 	a1 = TAILQ_FIRST(&pfa_table);
1738 	while (a1 != NULL) {
1739 		a2 = TAILQ_NEXT(a1, link);
1740 		free(a1);
1741 		a1 = a2;
1742 	}
1743 
1744 	/* And the list of labeled filter rules */
1745 	l1 = TAILQ_FIRST(&pfl_table);
1746 	while (l1 != NULL) {
1747 		l2 = TAILQ_NEXT(l1, link);
1748 		free(l1);
1749 		l1 = l2;
1750 	}
1751 
1752 	close(dev);
1753 	return (0);
1754 }
1755 
1756 static void
1757 pf_dump(void)
1758 {
1759 	pfi_refresh();
1760 	if (altq_enabled) {
1761 		pfq_refresh();
1762 	}
1763 	pft_refresh();
1764 	pfa_refresh();
1765 	pfl_refresh();
1766 
1767 	syslog(LOG_ERR, "Dump: pfi_table_age = %jd",
1768 	    (intmax_t)pfi_table_age);
1769 	syslog(LOG_ERR, "Dump: pfi_table_count = %d",
1770 	    pfi_table_count);
1771 
1772 	syslog(LOG_ERR, "Dump: pfq_table_age = %jd",
1773 	    (intmax_t)pfq_table_age);
1774 	syslog(LOG_ERR, "Dump: pfq_table_count = %d",
1775 	    pfq_table_count);
1776 
1777 	syslog(LOG_ERR, "Dump: pft_table_age = %jd",
1778 	    (intmax_t)pft_table_age);
1779 	syslog(LOG_ERR, "Dump: pft_table_count = %d",
1780 	    pft_table_count);
1781 
1782 	syslog(LOG_ERR, "Dump: pfa_table_age = %jd",
1783 	    (intmax_t)pfa_table_age);
1784 	syslog(LOG_ERR, "Dump: pfa_table_count = %d",
1785 	    pfa_table_count);
1786 
1787 	syslog(LOG_ERR, "Dump: pfl_table_age = %jd",
1788 	    (intmax_t)pfl_table_age);
1789 	syslog(LOG_ERR, "Dump: pfl_table_count = %d",
1790 	    pfl_table_count);
1791 }
1792 
1793 const struct snmp_module config = {
1794 	.comment = "This module implements a MIB for the pf packet filter.",
1795 	.init =		pf_init,
1796 	.fini =		pf_fini,
1797 	.tree =		pf_ctree,
1798 	.dump =		pf_dump,
1799 	.tree_size =	pf_CTREE_SIZE,
1800 };
1801