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