xref: /freebsd/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c (revision 9a14aa017b21c292740c00ee098195cd46642730)
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_pfInterfacesIfRefsState:
590 			val->v.uint32 = e->pfi.pfik_states;
591 			break;
592 		case LEAF_pfInterfacesIfRefsRule:
593 			val->v.uint32 = e->pfi.pfik_rules;
594 			break;
595 		case LEAF_pfInterfacesIf4BytesInPass:
596 			val->v.counter64 =
597 			    e->pfi.pfik_bytes[IPV4][IN][PASS];
598 			break;
599 		case LEAF_pfInterfacesIf4BytesInBlock:
600 			val->v.counter64 =
601 			    e->pfi.pfik_bytes[IPV4][IN][BLOCK];
602 			break;
603 		case LEAF_pfInterfacesIf4BytesOutPass:
604 			val->v.counter64 =
605 			    e->pfi.pfik_bytes[IPV4][OUT][PASS];
606 			break;
607 		case LEAF_pfInterfacesIf4BytesOutBlock:
608 			val->v.counter64 =
609 			    e->pfi.pfik_bytes[IPV4][OUT][BLOCK];
610 			break;
611 		case LEAF_pfInterfacesIf4PktsInPass:
612 			val->v.counter64 =
613 			    e->pfi.pfik_packets[IPV4][IN][PASS];
614 			break;
615 		case LEAF_pfInterfacesIf4PktsInBlock:
616 			val->v.counter64 =
617 			    e->pfi.pfik_packets[IPV4][IN][BLOCK];
618 			break;
619 		case LEAF_pfInterfacesIf4PktsOutPass:
620 			val->v.counter64 =
621 			    e->pfi.pfik_packets[IPV4][OUT][PASS];
622 			break;
623 		case LEAF_pfInterfacesIf4PktsOutBlock:
624 			val->v.counter64 =
625 			    e->pfi.pfik_packets[IPV4][OUT][BLOCK];
626 			break;
627 		case LEAF_pfInterfacesIf6BytesInPass:
628 			val->v.counter64 =
629 			    e->pfi.pfik_bytes[IPV6][IN][PASS];
630 			break;
631 		case LEAF_pfInterfacesIf6BytesInBlock:
632 			val->v.counter64 =
633 			    e->pfi.pfik_bytes[IPV6][IN][BLOCK];
634 			break;
635 		case LEAF_pfInterfacesIf6BytesOutPass:
636 			val->v.counter64 =
637 			    e->pfi.pfik_bytes[IPV6][OUT][PASS];
638 			break;
639 		case LEAF_pfInterfacesIf6BytesOutBlock:
640 			val->v.counter64 =
641 			    e->pfi.pfik_bytes[IPV6][OUT][BLOCK];
642 			break;
643 		case LEAF_pfInterfacesIf6PktsInPass:
644 			val->v.counter64 =
645 			    e->pfi.pfik_packets[IPV6][IN][PASS];
646 			break;
647 		case LEAF_pfInterfacesIf6PktsInBlock:
648 			val->v.counter64 =
649 			    e->pfi.pfik_packets[IPV6][IN][BLOCK];
650 			break;
651 		case LEAF_pfInterfacesIf6PktsOutPass:
652 			val->v.counter64 =
653 			    e->pfi.pfik_packets[IPV6][OUT][PASS];
654 			break;
655 		case LEAF_pfInterfacesIf6PktsOutBlock:
656 			val->v.counter64 =
657 			    e->pfi.pfik_packets[IPV6][OUT][BLOCK];
658 			break;
659 
660 		default:
661 			return (SNMP_ERR_NOSUCHNAME);
662 	}
663 
664 	return (SNMP_ERR_NOERROR);
665 }
666 
667 int
668 pf_tables(struct snmp_context __unused *ctx, struct snmp_value *val,
669 	u_int sub, u_int __unused vindex, enum snmp_op op)
670 {
671 	asn_subid_t	which = val->var.subs[sub - 1];
672 
673 	if (op == SNMP_OP_SET)
674 		return (SNMP_ERR_NOT_WRITEABLE);
675 
676 	if (op == SNMP_OP_GET) {
677 		if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
678 			if (pft_refresh() == -1)
679 			    return (SNMP_ERR_GENERR);
680 
681 		switch (which) {
682 			case LEAF_pfTablesTblNumber:
683 				val->v.uint32 = pft_table_count;
684 				break;
685 
686 			default:
687 				return (SNMP_ERR_NOSUCHNAME);
688 		}
689 
690 		return (SNMP_ERR_NOERROR);
691 	}
692 
693 	abort();
694 }
695 
696 int
697 pf_tbltable(struct snmp_context __unused *ctx, struct snmp_value *val,
698 	u_int sub, u_int __unused vindex, enum snmp_op op)
699 {
700 	asn_subid_t	which = val->var.subs[sub - 1];
701 	struct pft_entry *e = NULL;
702 
703 	if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
704 		pft_refresh();
705 
706 	switch (op) {
707 		case SNMP_OP_SET:
708 			return (SNMP_ERR_NOT_WRITEABLE);
709 		case SNMP_OP_GETNEXT:
710 			if ((e = NEXT_OBJECT_INT(&pft_table,
711 			    &val->var, sub)) == NULL)
712 				return (SNMP_ERR_NOSUCHNAME);
713 			val->var.len = sub + 1;
714 			val->var.subs[sub] = e->index;
715 			break;
716 		case SNMP_OP_GET:
717 			if (val->var.len - sub != 1)
718 				return (SNMP_ERR_NOSUCHNAME);
719 			if ((e = pft_table_find(val->var.subs[sub])) == NULL)
720 				return (SNMP_ERR_NOSUCHNAME);
721 			break;
722 
723 		case SNMP_OP_COMMIT:
724 		case SNMP_OP_ROLLBACK:
725 		default:
726 			abort();
727 	}
728 
729 	switch (which) {
730 		case LEAF_pfTablesTblDescr:
731 			return (string_get(val, e->pft.pfrts_name, -1));
732 		case LEAF_pfTablesTblCount:
733 			val->v.integer = e->pft.pfrts_cnt;
734 			break;
735 		case LEAF_pfTablesTblTZero:
736 			val->v.uint32 =
737 			    (time(NULL) - e->pft.pfrts_tzero) * 100;
738 			break;
739 		case LEAF_pfTablesTblRefsAnchor:
740 			val->v.integer =
741 			    e->pft.pfrts_refcnt[PFR_REFCNT_ANCHOR];
742 			break;
743 		case LEAF_pfTablesTblRefsRule:
744 			val->v.integer =
745 			    e->pft.pfrts_refcnt[PFR_REFCNT_RULE];
746 			break;
747 		case LEAF_pfTablesTblEvalMatch:
748 			val->v.counter64 = e->pft.pfrts_match;
749 			break;
750 		case LEAF_pfTablesTblEvalNoMatch:
751 			val->v.counter64 = e->pft.pfrts_nomatch;
752 			break;
753 		case LEAF_pfTablesTblBytesInPass:
754 			val->v.counter64 =
755 			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_PASS];
756 			break;
757 		case LEAF_pfTablesTblBytesInBlock:
758 			val->v.counter64 =
759 			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_BLOCK];
760 			break;
761 		case LEAF_pfTablesTblBytesInXPass:
762 			val->v.counter64 =
763 			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_XPASS];
764 			break;
765 		case LEAF_pfTablesTblBytesOutPass:
766 			val->v.counter64 =
767 			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_PASS];
768 			break;
769 		case LEAF_pfTablesTblBytesOutBlock:
770 			val->v.counter64 =
771 			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_BLOCK];
772 			break;
773 		case LEAF_pfTablesTblBytesOutXPass:
774 			val->v.counter64 =
775 			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_XPASS];
776 			break;
777 		case LEAF_pfTablesTblPktsInPass:
778 			val->v.counter64 =
779 			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_PASS];
780 			break;
781 		case LEAF_pfTablesTblPktsInBlock:
782 			val->v.counter64 =
783 			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_BLOCK];
784 			break;
785 		case LEAF_pfTablesTblPktsInXPass:
786 			val->v.counter64 =
787 			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_XPASS];
788 			break;
789 		case LEAF_pfTablesTblPktsOutPass:
790 			val->v.counter64 =
791 			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_PASS];
792 			break;
793 		case LEAF_pfTablesTblPktsOutBlock:
794 			val->v.counter64 =
795 			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_BLOCK];
796 			break;
797 		case LEAF_pfTablesTblPktsOutXPass:
798 			val->v.counter64 =
799 			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_XPASS];
800 			break;
801 
802 		default:
803 			return (SNMP_ERR_NOSUCHNAME);
804 	}
805 
806 	return (SNMP_ERR_NOERROR);
807 }
808 
809 int
810 pf_tbladdr(struct snmp_context __unused *ctx, struct snmp_value __unused *val,
811 	u_int __unused sub, u_int __unused vindex, enum snmp_op __unused op)
812 {
813 	asn_subid_t	which = val->var.subs[sub - 1];
814 	struct pfa_entry *e = NULL;
815 
816 	if ((time(NULL) - pfa_table_age) > PFA_TABLE_MAXAGE)
817 		pfa_refresh();
818 
819 	switch (op) {
820 		case SNMP_OP_SET:
821 			return (SNMP_ERR_NOT_WRITEABLE);
822 		case SNMP_OP_GETNEXT:
823 			if ((e = NEXT_OBJECT_INT(&pfa_table,
824 			    &val->var, sub)) == NULL)
825 				return (SNMP_ERR_NOSUCHNAME);
826 			val->var.len = sub + 1;
827 			val->var.subs[sub] = e->index;
828 			break;
829 		case SNMP_OP_GET:
830 			if (val->var.len - sub != 1)
831 				return (SNMP_ERR_NOSUCHNAME);
832 			if ((e = pfa_table_find(val->var.subs[sub])) == NULL)
833 				return (SNMP_ERR_NOSUCHNAME);
834 			break;
835 
836 		case SNMP_OP_COMMIT:
837 		case SNMP_OP_ROLLBACK:
838 		default:
839 			abort();
840 	}
841 
842 	switch (which) {
843 		case LEAF_pfTablesAddrNetType:
844 			if (e->pfas.pfras_a.pfra_af == AF_INET)
845 				val->v.integer = pfTablesAddrNetType_ipv4;
846 			else if (e->pfas.pfras_a.pfra_af == AF_INET6)
847 				val->v.integer = pfTablesAddrNetType_ipv6;
848 			else
849 				return (SNMP_ERR_GENERR);
850 			break;
851 		case LEAF_pfTablesAddrNet:
852 			if (e->pfas.pfras_a.pfra_af == AF_INET) {
853 				return (string_get(val,
854 				    (u_char *)&e->pfas.pfras_a.pfra_ip4addr, 4));
855 			} else if (e->pfas.pfras_a.pfra_af == AF_INET6)
856 				return (string_get(val,
857 				    (u_char *)&e->pfas.pfras_a.pfra_ip6addr, 16));
858 			else
859 				return (SNMP_ERR_GENERR);
860 			break;
861 		case LEAF_pfTablesAddrPrefix:
862 			val->v.integer = (int32_t) e->pfas.pfras_a.pfra_net;
863 			break;
864 		case LEAF_pfTablesAddrTZero:
865 			val->v.uint32 =
866 			    (time(NULL) - e->pfas.pfras_tzero) * 100;
867 			break;
868 		case LEAF_pfTablesAddrBytesInPass:
869 			val->v.counter64 =
870 			    e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_PASS];
871 			break;
872 		case LEAF_pfTablesAddrBytesInBlock:
873 			val->v.counter64 =
874 			    e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_BLOCK];
875 			break;
876 		case LEAF_pfTablesAddrBytesOutPass:
877 			val->v.counter64 =
878 			    e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_PASS];
879 			break;
880 		case LEAF_pfTablesAddrBytesOutBlock:
881 			val->v.counter64 =
882 			    e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_BLOCK];
883 			break;
884 		case LEAF_pfTablesAddrPktsInPass:
885 			val->v.counter64 =
886 			    e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_PASS];
887 			break;
888 		case LEAF_pfTablesAddrPktsInBlock:
889 			val->v.counter64 =
890 			    e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_BLOCK];
891 			break;
892 		case LEAF_pfTablesAddrPktsOutPass:
893 			val->v.counter64 =
894 			    e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_PASS];
895 			break;
896 		case LEAF_pfTablesAddrPktsOutBlock:
897 			val->v.counter64 =
898 			    e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_BLOCK];
899 			break;
900 		default:
901 			return (SNMP_ERR_NOSUCHNAME);
902 	}
903 
904 	return (SNMP_ERR_NOERROR);
905 }
906 
907 int
908 pf_altq(struct snmp_context __unused *ctx, struct snmp_value *val,
909 	u_int sub, u_int __unused vindex, enum snmp_op op)
910 {
911 	asn_subid_t	which = val->var.subs[sub - 1];
912 
913 	if (!altq_enabled)
914 	   return (SNMP_ERR_NOSUCHNAME);
915 
916 	if (op == SNMP_OP_SET)
917 		return (SNMP_ERR_NOT_WRITEABLE);
918 
919 	if (op == SNMP_OP_GET) {
920 		if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
921 			if (pfq_refresh() == -1)
922 			    return (SNMP_ERR_GENERR);
923 
924 		switch (which) {
925 			case LEAF_pfAltqQueueNumber:
926 				val->v.uint32 = pfq_table_count;
927 				break;
928 
929 			default:
930 				return (SNMP_ERR_NOSUCHNAME);
931 		}
932 
933 		return (SNMP_ERR_NOERROR);
934 	}
935 
936 	abort();
937 	return (SNMP_ERR_GENERR);
938 }
939 
940 int
941 pf_altqq(struct snmp_context __unused *ctx, struct snmp_value *val,
942 	u_int sub, u_int __unused vindex, enum snmp_op op)
943 {
944 	asn_subid_t	which = val->var.subs[sub - 1];
945 	struct pfq_entry *e = NULL;
946 
947 	if (!altq_enabled)
948 	   return (SNMP_ERR_NOSUCHNAME);
949 
950 	if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
951 		pfq_refresh();
952 
953 	switch (op) {
954 		case SNMP_OP_SET:
955 			return (SNMP_ERR_NOT_WRITEABLE);
956 		case SNMP_OP_GETNEXT:
957 			if ((e = NEXT_OBJECT_INT(&pfq_table,
958 			    &val->var, sub)) == NULL)
959 				return (SNMP_ERR_NOSUCHNAME);
960 			val->var.len = sub + 1;
961 			val->var.subs[sub] = e->index;
962 			break;
963 		case SNMP_OP_GET:
964 			if (val->var.len - sub != 1)
965 				return (SNMP_ERR_NOSUCHNAME);
966 			if ((e = pfq_table_find(val->var.subs[sub])) == NULL)
967 				return (SNMP_ERR_NOSUCHNAME);
968 			break;
969 
970 		case SNMP_OP_COMMIT:
971 		case SNMP_OP_ROLLBACK:
972 		default:
973 			abort();
974 	}
975 
976 	switch (which) {
977 		case LEAF_pfAltqQueueDescr:
978 			return (string_get(val, e->altq.qname, -1));
979 		case LEAF_pfAltqQueueParent:
980 			return (string_get(val, e->altq.parent, -1));
981 		case LEAF_pfAltqQueueScheduler:
982 			val->v.integer = e->altq.scheduler;
983 			break;
984 		case LEAF_pfAltqQueueBandwidth:
985 			val->v.uint32 = e->altq.bandwidth;
986 			break;
987 		case LEAF_pfAltqQueuePriority:
988 			val->v.integer = e->altq.priority;
989 			break;
990 		case LEAF_pfAltqQueueLimit:
991 			val->v.integer = e->altq.qlimit;
992 			break;
993 
994 		default:
995 			return (SNMP_ERR_NOSUCHNAME);
996 	}
997 
998 	return (SNMP_ERR_NOERROR);
999 }
1000 
1001 int
1002 pf_labels(struct snmp_context __unused *ctx, struct snmp_value *val,
1003 	u_int sub, u_int __unused vindex, enum snmp_op op)
1004 {
1005 	asn_subid_t	which = val->var.subs[sub - 1];
1006 
1007 	if (op == SNMP_OP_SET)
1008 		return (SNMP_ERR_NOT_WRITEABLE);
1009 
1010 	if (op == SNMP_OP_GET) {
1011 		if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE)
1012 			if (pfl_refresh() == -1)
1013 				return (SNMP_ERR_GENERR);
1014 
1015 		switch (which) {
1016 			case LEAF_pfLabelsLblNumber:
1017 				val->v.uint32 = pfl_table_count;
1018 				break;
1019 
1020 			default:
1021 				return (SNMP_ERR_NOSUCHNAME);
1022 		}
1023 
1024 		return (SNMP_ERR_NOERROR);
1025 	}
1026 
1027 	abort();
1028 	return (SNMP_ERR_GENERR);
1029 }
1030 
1031 int
1032 pf_lbltable(struct snmp_context __unused *ctx, struct snmp_value *val,
1033 	u_int sub, u_int __unused vindex, enum snmp_op op)
1034 {
1035 	asn_subid_t	which = val->var.subs[sub - 1];
1036 	struct pfl_entry *e = NULL;
1037 
1038 	if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE)
1039 		pfl_refresh();
1040 
1041 	switch (op) {
1042 		case SNMP_OP_SET:
1043 			return (SNMP_ERR_NOT_WRITEABLE);
1044 		case SNMP_OP_GETNEXT:
1045 			if ((e = NEXT_OBJECT_INT(&pfl_table,
1046 			    &val->var, sub)) == NULL)
1047 				return (SNMP_ERR_NOSUCHNAME);
1048 			val->var.len = sub + 1;
1049 			val->var.subs[sub] = e->index;
1050 			break;
1051 		case SNMP_OP_GET:
1052 			if (val->var.len - sub != 1)
1053 				return (SNMP_ERR_NOSUCHNAME);
1054 			if ((e = pfl_table_find(val->var.subs[sub])) == NULL)
1055 				return (SNMP_ERR_NOSUCHNAME);
1056 			break;
1057 
1058 		case SNMP_OP_COMMIT:
1059 		case SNMP_OP_ROLLBACK:
1060 		default:
1061 			abort();
1062 	}
1063 
1064 	switch (which) {
1065 		case LEAF_pfLabelsLblName:
1066 			return (string_get(val, e->name, -1));
1067 		case LEAF_pfLabelsLblEvals:
1068 			val->v.counter64 = e->evals;
1069 			break;
1070 		case LEAF_pfLabelsLblBytesIn:
1071 			val->v.counter64 = e->bytes[IN];
1072 			break;
1073 		case LEAF_pfLabelsLblBytesOut:
1074 			val->v.counter64 = e->bytes[OUT];
1075 			break;
1076 		case LEAF_pfLabelsLblPktsIn:
1077 			val->v.counter64 = e->pkts[IN];
1078 			break;
1079 		case LEAF_pfLabelsLblPktsOut:
1080 			val->v.counter64 = e->pkts[OUT];
1081 			break;
1082 		default:
1083 			return (SNMP_ERR_NOSUCHNAME);
1084 	}
1085 
1086 	return (SNMP_ERR_NOERROR);
1087 }
1088 
1089 static struct pfi_entry *
1090 pfi_table_find(u_int idx)
1091 {
1092 	struct pfi_entry *e;
1093 
1094 	TAILQ_FOREACH(e, &pfi_table, link)
1095 		if (e->index == idx)
1096 			return (e);
1097 	return (NULL);
1098 }
1099 
1100 static struct pfq_entry *
1101 pfq_table_find(u_int idx)
1102 {
1103 	struct pfq_entry *e;
1104 
1105 	TAILQ_FOREACH(e, &pfq_table, link)
1106 		if (e->index == idx)
1107 			return (e);
1108 	return (NULL);
1109 }
1110 
1111 static struct pft_entry *
1112 pft_table_find(u_int idx)
1113 {
1114 	struct pft_entry *e;
1115 
1116 	TAILQ_FOREACH(e, &pft_table, link)
1117 		if (e->index == idx)
1118 			return (e);
1119 	return (NULL);
1120 }
1121 
1122 static struct pfa_entry *
1123 pfa_table_find(u_int idx)
1124 {
1125 	struct pfa_entry *e;
1126 
1127 	TAILQ_FOREACH(e, &pfa_table, link)
1128 		if (e->index == idx)
1129 			return (e);
1130 	return (NULL);
1131 }
1132 
1133 static struct pfl_entry *
1134 pfl_table_find(u_int idx)
1135 {
1136 	struct pfl_entry *e;
1137 
1138 	TAILQ_FOREACH(e, &pfl_table, link)
1139 		if (e->index == idx)
1140 			return (e);
1141 
1142 	return (NULL);
1143 }
1144 
1145 static int
1146 pfi_refresh(void)
1147 {
1148 	struct pfioc_iface io;
1149 	struct pfi_kif *p = NULL;
1150 	struct pfi_entry *e;
1151 	int i, numifs = 1;
1152 
1153 	if (started && this_tick <= pf_tick)
1154 		return (0);
1155 
1156 	while (!TAILQ_EMPTY(&pfi_table)) {
1157 		e = TAILQ_FIRST(&pfi_table);
1158 		TAILQ_REMOVE(&pfi_table, e, link);
1159 		free(e);
1160 	}
1161 
1162 	bzero(&io, sizeof(io));
1163 	io.pfiio_esize = sizeof(struct pfi_kif);
1164 
1165 	for (;;) {
1166 		p = reallocf(p, numifs * sizeof(struct pfi_kif));
1167 		if (p == NULL) {
1168 			syslog(LOG_ERR, "pfi_refresh(): reallocf() numifs=%d: %s",
1169 			    numifs, strerror(errno));
1170 			goto err2;
1171 		}
1172 		io.pfiio_size = numifs;
1173 		io.pfiio_buffer = p;
1174 
1175 		if (ioctl(dev, DIOCIGETIFACES, &io)) {
1176 			syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s",
1177 			    strerror(errno));
1178 			goto err2;
1179 		}
1180 
1181 		if (numifs >= io.pfiio_size)
1182 			break;
1183 
1184 		numifs = io.pfiio_size;
1185 	}
1186 
1187 	for (i = 0; i < numifs; i++) {
1188 		e = malloc(sizeof(struct pfi_entry));
1189 		if (e == NULL)
1190 			goto err1;
1191 		e->index = i + 1;
1192 		memcpy(&e->pfi, p+i, sizeof(struct pfi_kif));
1193 		TAILQ_INSERT_TAIL(&pfi_table, e, link);
1194 	}
1195 
1196 	pfi_table_age = time(NULL);
1197 	pfi_table_count = numifs;
1198 	pf_tick = this_tick;
1199 
1200 	free(p);
1201 	return (0);
1202 
1203 err1:
1204 	while (!TAILQ_EMPTY(&pfi_table)) {
1205 		e = TAILQ_FIRST(&pfi_table);
1206 		TAILQ_REMOVE(&pfi_table, e, link);
1207 		free(e);
1208 	}
1209 err2:
1210 	free(p);
1211 	return(-1);
1212 }
1213 
1214 static int
1215 pfq_refresh(void)
1216 {
1217 	struct pfioc_altq pa;
1218 	struct pfq_entry *e;
1219 	int i, numqs, ticket;
1220 
1221 	if (started && this_tick <= pf_tick)
1222 		return (0);
1223 
1224 	while (!TAILQ_EMPTY(&pfq_table)) {
1225 		e = TAILQ_FIRST(&pfq_table);
1226 		TAILQ_REMOVE(&pfq_table, e, link);
1227 		free(e);
1228 	}
1229 
1230 	bzero(&pa, sizeof(pa));
1231 
1232 	if (ioctl(dev, DIOCGETALTQS, &pa)) {
1233 		syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s",
1234 		    strerror(errno));
1235 		return (-1);
1236 	}
1237 
1238 	numqs = pa.nr;
1239 	ticket = pa.ticket;
1240 
1241 	for (i = 0; i < numqs; i++) {
1242 		e = malloc(sizeof(struct pfq_entry));
1243 		if (e == NULL) {
1244 			syslog(LOG_ERR, "pfq_refresh(): "
1245 			    "malloc(): %s",
1246 			    strerror(errno));
1247 			goto err;
1248 		}
1249 		pa.ticket = ticket;
1250 		pa.nr = i;
1251 
1252 		if (ioctl(dev, DIOCGETALTQ, &pa)) {
1253 			syslog(LOG_ERR, "pfq_refresh(): "
1254 			    "ioctl(DIOCGETALTQ): %s",
1255 			    strerror(errno));
1256 			goto err;
1257 		}
1258 
1259 		if (pa.altq.qid > 0) {
1260 			memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq));
1261 			e->index = pa.altq.qid;
1262 			pfq_table_count = i;
1263 			INSERT_OBJECT_INT_LINK_INDEX(e, &pfq_table, link, index);
1264 		}
1265 	}
1266 
1267 	pfq_table_age = time(NULL);
1268 	pf_tick = this_tick;
1269 
1270 	return (0);
1271 err:
1272 	free(e);
1273 	while (!TAILQ_EMPTY(&pfq_table)) {
1274 		e = TAILQ_FIRST(&pfq_table);
1275 		TAILQ_REMOVE(&pfq_table, e, link);
1276 		free(e);
1277 	}
1278 	return(-1);
1279 }
1280 
1281 static int
1282 pfs_refresh(void)
1283 {
1284 	if (started && this_tick <= pf_tick)
1285 		return (0);
1286 
1287 	bzero(&pfs, sizeof(struct pf_status));
1288 
1289 	if (ioctl(dev, DIOCGETSTATUS, &pfs)) {
1290 		syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s",
1291 		    strerror(errno));
1292 		return (-1);
1293 	}
1294 
1295 	pf_tick = this_tick;
1296 	return (0);
1297 }
1298 
1299 static int
1300 pft_refresh(void)
1301 {
1302 	struct pfioc_table io;
1303 	struct pfr_tstats *t = NULL;
1304 	struct pft_entry *e;
1305 	int i, numtbls = 1;
1306 
1307 	if (started && this_tick <= pf_tick)
1308 		return (0);
1309 
1310 	while (!TAILQ_EMPTY(&pft_table)) {
1311 		e = TAILQ_FIRST(&pft_table);
1312 		TAILQ_REMOVE(&pft_table, e, link);
1313 		free(e);
1314 	}
1315 
1316 	bzero(&io, sizeof(io));
1317 	io.pfrio_esize = sizeof(struct pfr_tstats);
1318 
1319 	for (;;) {
1320 		t = reallocf(t, numtbls * sizeof(struct pfr_tstats));
1321 		if (t == NULL) {
1322 			syslog(LOG_ERR, "pft_refresh(): reallocf() numtbls=%d: %s",
1323 			    numtbls, strerror(errno));
1324 			goto err2;
1325 		}
1326 		io.pfrio_size = numtbls;
1327 		io.pfrio_buffer = t;
1328 
1329 		if (ioctl(dev, DIOCRGETTSTATS, &io)) {
1330 			syslog(LOG_ERR, "pft_refresh(): ioctl(): %s",
1331 			    strerror(errno));
1332 			goto err2;
1333 		}
1334 
1335 		if (numtbls >= io.pfrio_size)
1336 			break;
1337 
1338 		numtbls = io.pfrio_size;
1339 	}
1340 
1341 	for (i = 0; i < numtbls; i++) {
1342 		e = malloc(sizeof(struct pft_entry));
1343 		if (e == NULL)
1344 			goto err1;
1345 		e->index = i + 1;
1346 		memcpy(&e->pft, t+i, sizeof(struct pfr_tstats));
1347 		TAILQ_INSERT_TAIL(&pft_table, e, link);
1348 	}
1349 
1350 	pft_table_age = time(NULL);
1351 	pft_table_count = numtbls;
1352 	pf_tick = this_tick;
1353 
1354 	free(t);
1355 	return (0);
1356 err1:
1357 	while (!TAILQ_EMPTY(&pft_table)) {
1358 		e = TAILQ_FIRST(&pft_table);
1359 		TAILQ_REMOVE(&pft_table, e, link);
1360 		free(e);
1361 	}
1362 err2:
1363 	free(t);
1364 	return(-1);
1365 }
1366 
1367 static int
1368 pfa_table_addrs(u_int sidx, struct pfr_table *pt)
1369 {
1370 	struct pfioc_table io;
1371 	struct pfr_astats *t = NULL;
1372 	struct pfa_entry *e;
1373 	int i, numaddrs = 1;
1374 
1375 	if (pt == NULL)
1376 		return (-1);
1377 
1378 	memset(&io, 0, sizeof(io));
1379 	strlcpy(io.pfrio_table.pfrt_name, pt->pfrt_name,
1380 	    sizeof(io.pfrio_table.pfrt_name));
1381 
1382 	for (;;) {
1383 		t = reallocf(t, numaddrs * sizeof(struct pfr_astats));
1384 		if (t == NULL) {
1385 			syslog(LOG_ERR, "pfa_table_addrs(): reallocf(): %s",
1386 			    strerror(errno));
1387 			numaddrs = -1;
1388 			goto error;
1389 		}
1390 
1391 		memset(t, 0, sizeof(*t));
1392 		io.pfrio_size = numaddrs;
1393 		io.pfrio_buffer = t;
1394 		io.pfrio_esize = sizeof(struct pfr_astats);
1395 
1396 		if (ioctl(dev, DIOCRGETASTATS, &io)) {
1397 			syslog(LOG_ERR, "pfa_table_addrs(): ioctl() on %s: %s",
1398 			    pt->pfrt_name, strerror(errno));
1399 			numaddrs = -1;
1400 			break;
1401 		}
1402 
1403 		if (numaddrs >= io.pfrio_size)
1404 			break;
1405 
1406 		numaddrs = io.pfrio_size;
1407 	}
1408 
1409 	for (i = 0; i < numaddrs; i++) {
1410 		if ((t + i)->pfras_a.pfra_af != AF_INET &&
1411 		    (t + i)->pfras_a.pfra_af != AF_INET6) {
1412 			numaddrs = i;
1413 			break;
1414 		}
1415 
1416 		e = (struct pfa_entry *)malloc(sizeof(struct pfa_entry));
1417 		if (e == NULL) {
1418 			syslog(LOG_ERR, "pfa_table_addrs(): malloc(): %s",
1419 			    strerror(errno));
1420 			numaddrs = -1;
1421 			break;
1422 		}
1423 		e->index = sidx + i;
1424 		memcpy(&e->pfas, t + i, sizeof(struct pfr_astats));
1425 		TAILQ_INSERT_TAIL(&pfa_table, e, link);
1426 	}
1427 
1428 	free(t);
1429 error:
1430 	return (numaddrs);
1431 }
1432 
1433 static int
1434 pfa_refresh(void)
1435 {
1436 	struct pfioc_table io;
1437 	struct pfr_table *pt = NULL, *it = NULL;
1438 	struct pfa_entry *e;
1439 	int i, numtbls = 1, cidx, naddrs;
1440 
1441 	if (started && this_tick <= pf_tick)
1442 		return (0);
1443 
1444 	while (!TAILQ_EMPTY(&pfa_table)) {
1445 		e = TAILQ_FIRST(&pfa_table);
1446 		TAILQ_REMOVE(&pfa_table, e, link);
1447 		free(e);
1448 	}
1449 
1450 	memset(&io, 0, sizeof(io));
1451 	io.pfrio_esize = sizeof(struct pfr_table);
1452 
1453 	for (;;) {
1454 		pt = reallocf(pt, numtbls * sizeof(struct pfr_table));
1455 		if (pt == NULL) {
1456 			syslog(LOG_ERR, "pfa_refresh(): reallocf() %s",
1457 			    strerror(errno));
1458 			return (-1);
1459 		}
1460 		memset(pt, 0, sizeof(*pt));
1461 		io.pfrio_size = numtbls;
1462 		io.pfrio_buffer = pt;
1463 
1464 		if (ioctl(dev, DIOCRGETTABLES, &io)) {
1465 			syslog(LOG_ERR, "pfa_refresh(): ioctl(): %s",
1466 			    strerror(errno));
1467 			goto err2;
1468 		}
1469 
1470 		if (numtbls >= io.pfrio_size)
1471 			break;
1472 
1473 		numtbls = io.pfrio_size;
1474 	}
1475 
1476 	cidx = 1;
1477 
1478 	for (it = pt, i = 0; i < numtbls; it++, i++) {
1479 		/*
1480 		 * Skip the table if not active - ioctl(DIOCRGETASTATS) will
1481 		 * return ESRCH for this entry anyway.
1482 		 */
1483 		if (!(it->pfrt_flags & PFR_TFLAG_ACTIVE))
1484 			continue;
1485 
1486 		if ((naddrs = pfa_table_addrs(cidx, it)) < 0)
1487 			goto err1;
1488 
1489 		cidx += naddrs;
1490 	}
1491 
1492 	pfa_table_age = time(NULL);
1493 	pfa_table_count = cidx;
1494 	pf_tick = this_tick;
1495 
1496 	free(pt);
1497 	return (0);
1498 err1:
1499 	while (!TAILQ_EMPTY(&pfa_table)) {
1500 		e = TAILQ_FIRST(&pfa_table);
1501 		TAILQ_REMOVE(&pfa_table, e, link);
1502 		free(e);
1503 	}
1504 
1505 err2:
1506 	free(pt);
1507 	return (-1);
1508 }
1509 
1510 static int
1511 pfl_scan_ruleset(const char *path)
1512 {
1513 	struct pfioc_rule pr;
1514 	struct pfl_entry *e;
1515 	u_int32_t nr, i;
1516 
1517 	bzero(&pr, sizeof(pr));
1518 	strlcpy(pr.anchor, path, sizeof(pr.anchor));
1519 	pr.rule.action = PF_PASS;
1520 	if (ioctl(dev, DIOCGETRULES, &pr)) {
1521 		syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULES): %s",
1522 		    strerror(errno));
1523 		goto err;
1524 	}
1525 
1526 	for (nr = pr.nr, i = 0; i < nr; i++) {
1527 		pr.nr = i;
1528 		if (ioctl(dev, DIOCGETRULE, &pr)) {
1529 			syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULE):"
1530 			    " %s", strerror(errno));
1531 			goto err;
1532 		}
1533 
1534 		if (pr.rule.label[0]) {
1535 			e = (struct pfl_entry *)malloc(sizeof(*e));
1536 			if (e == NULL)
1537 				goto err;
1538 
1539 			strlcpy(e->name, path, sizeof(e->name));
1540 			if (path[0])
1541 				strlcat(e->name, "/", sizeof(e->name));
1542 			strlcat(e->name, pr.rule.label, sizeof(e->name));
1543 
1544 			e->evals = pr.rule.evaluations;
1545 			e->bytes[IN] = pr.rule.bytes[IN];
1546 			e->bytes[OUT] = pr.rule.bytes[OUT];
1547 			e->pkts[IN] = pr.rule.packets[IN];
1548 			e->pkts[OUT] = pr.rule.packets[OUT];
1549 			e->index = ++pfl_table_count;
1550 
1551 			TAILQ_INSERT_TAIL(&pfl_table, e, link);
1552 		}
1553 	}
1554 
1555 	return (0);
1556 
1557 err:
1558 	return (-1);
1559 }
1560 
1561 static int
1562 pfl_walk_rulesets(const char *path)
1563 {
1564 	struct pfioc_ruleset prs;
1565 	char newpath[MAXPATHLEN];
1566 	u_int32_t nr, i;
1567 
1568 	if (pfl_scan_ruleset(path))
1569 		goto err;
1570 
1571 	bzero(&prs, sizeof(prs));
1572 	strlcpy(prs.path, path, sizeof(prs.path));
1573 	if (ioctl(dev, DIOCGETRULESETS, &prs)) {
1574 		syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESETS): %s",
1575 		    strerror(errno));
1576 		goto err;
1577 	}
1578 
1579 	for (nr = prs.nr, i = 0; i < nr; i++) {
1580 		prs.nr = i;
1581 		if (ioctl(dev, DIOCGETRULESET, &prs)) {
1582 			syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESET):"
1583 			    " %s", strerror(errno));
1584 			goto err;
1585 		}
1586 
1587 		if (strcmp(prs.name, PF_RESERVED_ANCHOR) == 0)
1588 			continue;
1589 
1590 		strlcpy(newpath, path, sizeof(newpath));
1591 		if (path[0])
1592 			strlcat(newpath, "/", sizeof(newpath));
1593 
1594 		strlcat(newpath, prs.name, sizeof(newpath));
1595 		if (pfl_walk_rulesets(newpath))
1596 			goto err;
1597 	}
1598 
1599 	return (0);
1600 
1601 err:
1602 	return (-1);
1603 }
1604 
1605 static int
1606 pfl_refresh(void)
1607 {
1608 	struct pfl_entry *e;
1609 
1610 	if (started && this_tick <= pf_tick)
1611 		return (0);
1612 
1613 	while (!TAILQ_EMPTY(&pfl_table)) {
1614 		e = TAILQ_FIRST(&pfl_table);
1615 		TAILQ_REMOVE(&pfl_table, e, link);
1616 		free(e);
1617 	}
1618 	pfl_table_count = 0;
1619 
1620 	if (pfl_walk_rulesets(""))
1621 		goto err;
1622 
1623 	pfl_table_age = time(NULL);
1624 	pf_tick = this_tick;
1625 
1626 	return (0);
1627 
1628 err:
1629 	while (!TAILQ_EMPTY(&pfl_table)) {
1630 		e = TAILQ_FIRST(&pfl_table);
1631 		TAILQ_REMOVE(&pfl_table, e, link);
1632 		free(e);
1633 	}
1634 	pfl_table_count = 0;
1635 
1636 	return (-1);
1637 }
1638 
1639 /*
1640  * check whether altq support is enabled in kernel
1641  */
1642 
1643 static int
1644 altq_is_enabled(int pfdev)
1645 {
1646         struct pfioc_altq pa;
1647 
1648 	errno = 0;
1649         if (ioctl(pfdev, DIOCGETALTQS, &pa)) {
1650                 if (errno == ENODEV) {
1651 			syslog(LOG_INFO, "No ALTQ support in kernel\n"
1652 			    "ALTQ related functions disabled\n");
1653                         return (0);
1654                 } else
1655                         syslog(LOG_ERR, "DIOCGETALTQS returned an error: %s",
1656 			    strerror(errno));
1657 			return (-1);
1658         }
1659         return (1);
1660 }
1661 
1662 /*
1663  * Implement the bsnmpd module interface
1664  */
1665 static int
1666 pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[])
1667 {
1668 	module = mod;
1669 
1670 	if ((dev = open("/dev/pf", O_RDONLY)) == -1) {
1671 		syslog(LOG_ERR, "pf_init(): open(): %s\n",
1672 		    strerror(errno));
1673 		return (-1);
1674 	}
1675 
1676 	if ((altq_enabled = altq_is_enabled(dev)) == -1) {
1677 		syslog(LOG_ERR, "pf_init(): altq test failed");
1678 		return (-1);
1679 	}
1680 
1681 	/* Prepare internal state */
1682 	TAILQ_INIT(&pfi_table);
1683 	TAILQ_INIT(&pfq_table);
1684 	TAILQ_INIT(&pft_table);
1685 	TAILQ_INIT(&pfa_table);
1686 	TAILQ_INIT(&pfl_table);
1687 
1688 	pfi_refresh();
1689 	if (altq_enabled) {
1690 		pfq_refresh();
1691 	}
1692 
1693 	pfs_refresh();
1694 	pft_refresh();
1695 	pfa_refresh();
1696 	pfl_refresh();
1697 
1698 	started = 1;
1699 
1700 	return (0);
1701 }
1702 
1703 static int
1704 pf_fini(void)
1705 {
1706 	struct pfi_entry *i1, *i2;
1707 	struct pfq_entry *q1, *q2;
1708 	struct pft_entry *t1, *t2;
1709 	struct pfa_entry *a1, *a2;
1710 	struct pfl_entry *l1, *l2;
1711 
1712 	/* Empty the list of interfaces */
1713 	i1 = TAILQ_FIRST(&pfi_table);
1714 	while (i1 != NULL) {
1715 		i2 = TAILQ_NEXT(i1, link);
1716 		free(i1);
1717 		i1 = i2;
1718 	}
1719 
1720 	/* List of queues */
1721 	q1 = TAILQ_FIRST(&pfq_table);
1722 	while (q1 != NULL) {
1723 		q2 = TAILQ_NEXT(q1, link);
1724 		free(q1);
1725 		q1 = q2;
1726 	}
1727 
1728 	/* List of tables */
1729 	t1 = TAILQ_FIRST(&pft_table);
1730 	while (t1 != NULL) {
1731 		t2 = TAILQ_NEXT(t1, link);
1732 		free(t1);
1733 		t1 = t2;
1734 	}
1735 
1736 	/* List of table addresses */
1737 	a1 = TAILQ_FIRST(&pfa_table);
1738 	while (a1 != NULL) {
1739 		a2 = TAILQ_NEXT(a1, link);
1740 		free(a1);
1741 		a1 = a2;
1742 	}
1743 
1744 	/* And the list of labeled filter rules */
1745 	l1 = TAILQ_FIRST(&pfl_table);
1746 	while (l1 != NULL) {
1747 		l2 = TAILQ_NEXT(l1, link);
1748 		free(l1);
1749 		l1 = l2;
1750 	}
1751 
1752 	close(dev);
1753 	return (0);
1754 }
1755 
1756 static void
1757 pf_dump(void)
1758 {
1759 	pfi_refresh();
1760 	if (altq_enabled) {
1761 		pfq_refresh();
1762 	}
1763 	pft_refresh();
1764 	pfa_refresh();
1765 	pfl_refresh();
1766 
1767 	syslog(LOG_ERR, "Dump: pfi_table_age = %jd",
1768 	    (intmax_t)pfi_table_age);
1769 	syslog(LOG_ERR, "Dump: pfi_table_count = %d",
1770 	    pfi_table_count);
1771 
1772 	syslog(LOG_ERR, "Dump: pfq_table_age = %jd",
1773 	    (intmax_t)pfq_table_age);
1774 	syslog(LOG_ERR, "Dump: pfq_table_count = %d",
1775 	    pfq_table_count);
1776 
1777 	syslog(LOG_ERR, "Dump: pft_table_age = %jd",
1778 	    (intmax_t)pft_table_age);
1779 	syslog(LOG_ERR, "Dump: pft_table_count = %d",
1780 	    pft_table_count);
1781 
1782 	syslog(LOG_ERR, "Dump: pfa_table_age = %jd",
1783 	    (intmax_t)pfa_table_age);
1784 	syslog(LOG_ERR, "Dump: pfa_table_count = %d",
1785 	    pfa_table_count);
1786 
1787 	syslog(LOG_ERR, "Dump: pfl_table_age = %jd",
1788 	    (intmax_t)pfl_table_age);
1789 	syslog(LOG_ERR, "Dump: pfl_table_count = %d",
1790 	    pfl_table_count);
1791 }
1792 
1793 const struct snmp_module config = {
1794 	.comment = "This module implements a MIB for the pf packet filter.",
1795 	.init =		pf_init,
1796 	.fini =		pf_fini,
1797 	.tree =		pf_ctree,
1798 	.dump =		pf_dump,
1799 	.tree_size =	pf_CTREE_SIZE,
1800 };
1801