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