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