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