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: stable/10/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c 357523 2020-02-04 19:46:29Z dim $
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
47struct lmodule *module;
48
49static int dev = -1;
50static int started;
51static uint64_t pf_tick;
52
53static struct pf_status pfs;
54
55enum { IN, OUT };
56enum { IPV4, IPV6 };
57enum { PASS, BLOCK };
58
59#define PFI_IFTYPE_GROUP	0
60#define PFI_IFTYPE_INSTANCE	1
61#define PFI_IFTYPE_DETACHED	2
62
63struct pfi_entry {
64	struct pfi_kif	pfi;
65	u_int		index;
66	TAILQ_ENTRY(pfi_entry) link;
67};
68TAILQ_HEAD(pfi_table, pfi_entry);
69
70static struct pfi_table pfi_table;
71static time_t pfi_table_age;
72static int pfi_table_count;
73
74#define PFI_TABLE_MAXAGE	5
75
76struct pft_entry {
77	struct pfr_tstats pft;
78	u_int		index;
79	TAILQ_ENTRY(pft_entry) link;
80};
81TAILQ_HEAD(pft_table, pft_entry);
82
83static struct pft_table pft_table;
84static time_t pft_table_age;
85static int pft_table_count;
86
87#define PFT_TABLE_MAXAGE	5
88
89struct pfa_entry {
90	struct pfr_astats pfas;
91	u_int		index;
92	TAILQ_ENTRY(pfa_entry) link;
93};
94TAILQ_HEAD(pfa_table, pfa_entry);
95
96static struct pfa_table pfa_table;
97static time_t pfa_table_age;
98static int pfa_table_count;
99
100#define	PFA_TABLE_MAXAGE	5
101
102struct pfq_entry {
103	struct pf_altq	altq;
104	u_int		index;
105	TAILQ_ENTRY(pfq_entry) link;
106};
107TAILQ_HEAD(pfq_table, pfq_entry);
108
109static struct pfq_table pfq_table;
110static time_t pfq_table_age;
111static int pfq_table_count;
112
113static int altq_enabled = 0;
114
115#define PFQ_TABLE_MAXAGE	5
116
117struct 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};
125TAILQ_HEAD(pfl_table, pfl_entry);
126
127static struct pfl_table pfl_table;
128static time_t pfl_table_age;
129static int pfl_table_count;
130
131#define	PFL_TABLE_MAXAGE	5
132
133/* Forward declarations */
134static int pfi_refresh(void);
135static int pfq_refresh(void);
136static int pfs_refresh(void);
137static int pft_refresh(void);
138static int pfa_refresh(void);
139static int pfl_refresh(void);
140static struct pfi_entry * pfi_table_find(u_int idx);
141static struct pfq_entry * pfq_table_find(u_int idx);
142static struct pft_entry * pft_table_find(u_int idx);
143static struct pfa_entry * pfa_table_find(u_int idx);
144static struct pfl_entry * pfl_table_find(u_int idx);
145
146static int altq_is_enabled(int pfdevice);
147
148int
149pf_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
189int
190pf_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
232int
233pf_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
272int
273pf_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
312int
313pf_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
354int
355pf_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
444int
445pf_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
517int
518pf_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
546int
547pf_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
664int
665pf_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
693int
694pf_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
806int
807pf_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
904int
905pf_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
937int
938pf_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
998int
999pf_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
1028int
1029pf_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
1086static struct pfi_entry *
1087pfi_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
1097static struct pfq_entry *
1098pfq_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
1108static struct pft_entry *
1109pft_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
1119static struct pfa_entry *
1120pfa_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
1130static struct pfl_entry *
1131pfl_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
1142static int
1143pfi_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
1200err1:
1201	while (!TAILQ_EMPTY(&pfi_table)) {
1202		e = TAILQ_FIRST(&pfi_table);
1203		TAILQ_REMOVE(&pfi_table, e, link);
1204		free(e);
1205	}
1206err2:
1207	free(p);
1208	return(-1);
1209}
1210
1211static int
1212pfq_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);
1268err:
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
1278static int
1279pfs_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
1296static int
1297pft_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);
1353err1:
1354	while (!TAILQ_EMPTY(&pft_table)) {
1355		e = TAILQ_FIRST(&pft_table);
1356		TAILQ_REMOVE(&pft_table, e, link);
1357		free(e);
1358	}
1359err2:
1360	free(t);
1361	return(-1);
1362}
1363
1364static int
1365pfa_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);
1426error:
1427	return (numaddrs);
1428}
1429
1430static int
1431pfa_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);
1495err1:
1496	while (!TAILQ_EMPTY(&pfa_table)) {
1497		e = TAILQ_FIRST(&pfa_table);
1498		TAILQ_REMOVE(&pfa_table, e, link);
1499		free(e);
1500	}
1501
1502err2:
1503	free(pt);
1504	return (-1);
1505}
1506
1507static int
1508pfl_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
1554err:
1555	return (-1);
1556}
1557
1558static int
1559pfl_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
1598err:
1599	return (-1);
1600}
1601
1602static int
1603pfl_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
1625err:
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
1640static int
1641altq_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	}
1657	return (1);
1658}
1659
1660/*
1661 * Implement the bsnmpd module interface
1662 */
1663static int
1664pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[])
1665{
1666	module = mod;
1667
1668	if ((dev = open("/dev/pf", O_RDONLY)) == -1) {
1669		syslog(LOG_ERR, "pf_init(): open(): %s\n",
1670		    strerror(errno));
1671		return (-1);
1672	}
1673
1674	if ((altq_enabled = altq_is_enabled(dev)) == -1) {
1675		syslog(LOG_ERR, "pf_init(): altq test failed");
1676		return (-1);
1677	}
1678
1679	/* Prepare internal state */
1680	TAILQ_INIT(&pfi_table);
1681	TAILQ_INIT(&pfq_table);
1682	TAILQ_INIT(&pft_table);
1683	TAILQ_INIT(&pfa_table);
1684	TAILQ_INIT(&pfl_table);
1685
1686	pfi_refresh();
1687	if (altq_enabled) {
1688		pfq_refresh();
1689	}
1690
1691	pfs_refresh();
1692	pft_refresh();
1693	pfa_refresh();
1694	pfl_refresh();
1695
1696	started = 1;
1697
1698	return (0);
1699}
1700
1701static int
1702pf_fini(void)
1703{
1704	struct pfi_entry *i1, *i2;
1705	struct pfq_entry *q1, *q2;
1706	struct pft_entry *t1, *t2;
1707	struct pfa_entry *a1, *a2;
1708	struct pfl_entry *l1, *l2;
1709
1710	/* Empty the list of interfaces */
1711	i1 = TAILQ_FIRST(&pfi_table);
1712	while (i1 != NULL) {
1713		i2 = TAILQ_NEXT(i1, link);
1714		free(i1);
1715		i1 = i2;
1716	}
1717
1718	/* List of queues */
1719	q1 = TAILQ_FIRST(&pfq_table);
1720	while (q1 != NULL) {
1721		q2 = TAILQ_NEXT(q1, link);
1722		free(q1);
1723		q1 = q2;
1724	}
1725
1726	/* List of tables */
1727	t1 = TAILQ_FIRST(&pft_table);
1728	while (t1 != NULL) {
1729		t2 = TAILQ_NEXT(t1, link);
1730		free(t1);
1731		t1 = t2;
1732	}
1733
1734	/* List of table addresses */
1735	a1 = TAILQ_FIRST(&pfa_table);
1736	while (a1 != NULL) {
1737		a2 = TAILQ_NEXT(a1, link);
1738		free(a1);
1739		a1 = a2;
1740	}
1741
1742	/* And the list of labeled filter rules */
1743	l1 = TAILQ_FIRST(&pfl_table);
1744	while (l1 != NULL) {
1745		l2 = TAILQ_NEXT(l1, link);
1746		free(l1);
1747		l1 = l2;
1748	}
1749
1750	close(dev);
1751	return (0);
1752}
1753
1754static void
1755pf_dump(void)
1756{
1757	pfi_refresh();
1758	if (altq_enabled) {
1759		pfq_refresh();
1760	}
1761	pft_refresh();
1762	pfa_refresh();
1763	pfl_refresh();
1764
1765	syslog(LOG_ERR, "Dump: pfi_table_age = %jd",
1766	    (intmax_t)pfi_table_age);
1767	syslog(LOG_ERR, "Dump: pfi_table_count = %d",
1768	    pfi_table_count);
1769
1770	syslog(LOG_ERR, "Dump: pfq_table_age = %jd",
1771	    (intmax_t)pfq_table_age);
1772	syslog(LOG_ERR, "Dump: pfq_table_count = %d",
1773	    pfq_table_count);
1774
1775	syslog(LOG_ERR, "Dump: pft_table_age = %jd",
1776	    (intmax_t)pft_table_age);
1777	syslog(LOG_ERR, "Dump: pft_table_count = %d",
1778	    pft_table_count);
1779
1780	syslog(LOG_ERR, "Dump: pfa_table_age = %jd",
1781	    (intmax_t)pfa_table_age);
1782	syslog(LOG_ERR, "Dump: pfa_table_count = %d",
1783	    pfa_table_count);
1784
1785	syslog(LOG_ERR, "Dump: pfl_table_age = %jd",
1786	    (intmax_t)pfl_table_age);
1787	syslog(LOG_ERR, "Dump: pfl_table_count = %d",
1788	    pfl_table_count);
1789}
1790
1791const struct snmp_module config = {
1792	.comment = "This module implements a MIB for the pf packet filter.",
1793	.init =		pf_init,
1794	.fini =		pf_fini,
1795	.tree =		pf_ctree,
1796	.dump =		pf_dump,
1797	.tree_size =	pf_CTREE_SIZE,
1798};
1799