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