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