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