pf_snmp.c revision 146531
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 146531 2005-05-23 11:41:14Z philip $
27 */
28
29#include <bsnmp/snmpmod.h>
30
31#include <net/pfvar.h>
32#include <sys/ioctl.h>
33
34#include <errno.h>
35#include <fcntl.h>
36#include <stdint.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <syslog.h>
41#include <unistd.h>
42
43#include "pf_oid.h"
44#include "pf_tree.h"
45
46struct lmodule *module;
47
48static int dev = -1;
49static int started;
50static uint64_t pf_tick;
51
52static struct pf_status pfs;
53
54enum { IN, OUT };
55enum { IPV4, IPV6 };
56enum { PASS, BLOCK };
57
58#define PFI_IFTYPE_GROUP	0
59#define PFI_IFTYPE_INSTANCE	1
60#define PFI_IFTYPE_DETACHED	2
61
62struct pfi_entry {
63	struct pfi_if	pfi;
64	u_int		index;
65	TAILQ_ENTRY(pfi_entry) link;
66};
67TAILQ_HEAD(pfi_table, pfi_entry);
68
69static struct pfi_table pfi_table;
70static time_t pfi_table_age;
71static int pfi_table_count;
72
73#define PFI_TABLE_MAXAGE	5
74
75struct pft_entry {
76	struct pfr_tstats pft;
77	u_int		index;
78	TAILQ_ENTRY(pft_entry) link;
79};
80TAILQ_HEAD(pft_table, pft_entry);
81
82static struct pft_table pft_table;
83static time_t pft_table_age;
84static int pft_table_count;
85
86#define PFT_TABLE_MAXAGE	5
87
88struct pfq_entry {
89	struct pf_altq	altq;
90	u_int		index;
91	TAILQ_ENTRY(pfq_entry) link;
92};
93TAILQ_HEAD(pfq_table, pfq_entry);
94
95static struct pfq_table pfq_table;
96static time_t pfq_table_age;
97static int pfq_table_count;
98
99#define PFQ_TABLE_MAXAGE	5
100
101/* Forward declarations */
102static int pfi_refresh(void);
103static int pfq_refresh(void);
104static int pfs_refresh(void);
105static int pft_refresh(void);
106static struct pfi_entry * pfi_table_find(u_int idx);
107static struct pfq_entry * pfq_table_find(u_int idx);
108static struct pft_entry * pft_table_find(u_int idx);
109
110int
111pf_status(struct snmp_context __unused *ctx, struct snmp_value *val,
112	u_int sub, u_int __unused vindex, enum snmp_op op)
113{
114	asn_subid_t	which = val->var.subs[sub - 1];
115	time_t		runtime;
116	unsigned char	str[128];
117
118	if (op == SNMP_OP_SET)
119		return (SNMP_ERR_NOT_WRITEABLE);
120
121	if (op == SNMP_OP_GET) {
122		if (pfs_refresh() == -1)
123			return (SNMP_ERR_GENERR);
124
125		switch (which) {
126			case LEAF_pfStatusRunning:
127			    val->v.uint32 = pfs.running;
128			    break;
129			case LEAF_pfStatusRuntime:
130			    runtime = (pfs.since > 0) ?
131				time(NULL) - pfs.since : 0;
132			    val->v.uint32 = runtime * 100;
133			    break;
134			case LEAF_pfStatusDebug:
135			    val->v.uint32 = pfs.debug;
136			    break;
137			case LEAF_pfStatusHostId:
138			    sprintf(str, "0x%08x", ntohl(pfs.hostid));
139			    return (string_get(val, str, strlen(str)));
140
141			default:
142			    return (SNMP_ERR_NOSUCHNAME);
143		}
144
145		return (SNMP_ERR_NOERROR);
146	}
147
148	abort();
149}
150
151int
152pf_counter(struct snmp_context __unused *ctx, struct snmp_value *val,
153	u_int sub, u_int __unused vindex, enum snmp_op op)
154{
155	asn_subid_t	which = val->var.subs[sub - 1];
156
157	if (op == SNMP_OP_SET)
158		return (SNMP_ERR_NOT_WRITEABLE);
159
160	if (op == SNMP_OP_GET) {
161		if (pfs_refresh() == -1)
162			return (SNMP_ERR_GENERR);
163
164		switch (which) {
165			case LEAF_pfCounterMatch:
166				val->v.counter64 = pfs.counters[PFRES_MATCH];
167				break;
168			case LEAF_pfCounterBadOffset:
169				val->v.counter64 = pfs.counters[PFRES_BADOFF];
170				break;
171			case LEAF_pfCounterFragment:
172				val->v.counter64 = pfs.counters[PFRES_FRAG];
173				break;
174			case LEAF_pfCounterShort:
175				val->v.counter64 = pfs.counters[PFRES_SHORT];
176				break;
177			case LEAF_pfCounterNormalize:
178				val->v.counter64 = pfs.counters[PFRES_NORM];
179				break;
180			case LEAF_pfCounterMemDrop:
181				val->v.counter64 = pfs.counters[PFRES_MEMORY];
182				break;
183
184			default:
185				return (SNMP_ERR_NOSUCHNAME);
186		}
187
188		return (SNMP_ERR_NOERROR);
189	}
190
191	abort();
192}
193
194int
195pf_statetable(struct snmp_context __unused *ctx, struct snmp_value *val,
196	u_int sub, u_int __unused vindex, enum snmp_op op)
197{
198	asn_subid_t	which = val->var.subs[sub - 1];
199
200	if (op == SNMP_OP_SET)
201		return (SNMP_ERR_NOT_WRITEABLE);
202
203	if (op == SNMP_OP_GET) {
204		if (pfs_refresh() == -1)
205			return (SNMP_ERR_GENERR);
206
207		switch (which) {
208			case LEAF_pfStateTableCount:
209				val->v.uint32 = pfs.states;
210				break;
211			case LEAF_pfStateTableSearches:
212				val->v.counter64 =
213				    pfs.fcounters[FCNT_STATE_SEARCH];
214				break;
215			case LEAF_pfStateTableInserts:
216				val->v.counter64 =
217				    pfs.fcounters[FCNT_STATE_INSERT];
218				break;
219			case LEAF_pfStateTableRemovals:
220				val->v.counter64 =
221				    pfs.fcounters[FCNT_STATE_REMOVALS];
222				break;
223
224			default:
225				return (SNMP_ERR_NOSUCHNAME);
226		}
227
228		return (SNMP_ERR_NOERROR);
229	}
230
231	abort();
232}
233
234int
235pf_srcnodes(struct snmp_context __unused *ctx, struct snmp_value *val,
236	u_int sub, u_int __unused vindex, enum snmp_op op)
237{
238	asn_subid_t	which = val->var.subs[sub - 1];
239
240	if (op == SNMP_OP_SET)
241		return (SNMP_ERR_NOT_WRITEABLE);
242
243	if (op == SNMP_OP_GET) {
244		if (pfs_refresh() == -1)
245			return (SNMP_ERR_GENERR);
246
247		switch (which) {
248			case LEAF_pfSrcNodesCount:
249				val->v.uint32 = pfs.src_nodes;
250				break;
251			case LEAF_pfSrcNodesSearches:
252				val->v.counter64 =
253				    pfs.scounters[SCNT_SRC_NODE_SEARCH];
254				break;
255			case LEAF_pfSrcNodesInserts:
256				val->v.counter64 =
257				    pfs.scounters[SCNT_SRC_NODE_INSERT];
258				break;
259			case LEAF_pfSrcNodesRemovals:
260				val->v.counter64 =
261				    pfs.scounters[SCNT_SRC_NODE_REMOVALS];
262				break;
263
264			default:
265				return (SNMP_ERR_NOSUCHNAME);
266		}
267
268		return (SNMP_ERR_NOERROR);
269	}
270
271	abort();
272}
273
274int
275pf_limits(struct snmp_context __unused *ctx, struct snmp_value *val,
276	u_int sub, u_int __unused vindex, enum snmp_op op)
277{
278	asn_subid_t		which = val->var.subs[sub - 1];
279	struct pfioc_limit	pl;
280
281	if (op == SNMP_OP_SET)
282		return (SNMP_ERR_NOT_WRITEABLE);
283
284	if (op == SNMP_OP_GET) {
285		bzero(&pl, sizeof(struct pfioc_limit));
286
287		switch (which) {
288			case LEAF_pfLimitsStates:
289				pl.index = PF_LIMIT_STATES;
290				break;
291			case LEAF_pfLimitsSrcNodes:
292				pl.index = PF_LIMIT_SRC_NODES;
293				break;
294			case LEAF_pfLimitsFrags:
295				pl.index = PF_LIMIT_FRAGS;
296				break;
297
298			default:
299				return (SNMP_ERR_NOSUCHNAME);
300		}
301
302		if (ioctl(dev, DIOCGETLIMIT, &pl)) {
303			syslog(LOG_ERR, "pf_limits(): ioctl(): %s",
304			    strerror(errno));
305			return (SNMP_ERR_GENERR);
306		}
307
308		val->v.uint32 = pl.limit;
309
310		return (SNMP_ERR_NOERROR);
311	}
312
313	abort();
314}
315
316int
317pf_timeouts(struct snmp_context __unused *ctx, struct snmp_value *val,
318	u_int sub, u_int __unused vindex, enum snmp_op op)
319{
320	asn_subid_t	which = val->var.subs[sub - 1];
321	struct pfioc_tm	pt;
322
323	if (op == SNMP_OP_SET)
324		return (SNMP_ERR_NOT_WRITEABLE);
325
326	if (op == SNMP_OP_GET) {
327		bzero(&pt, sizeof(struct pfioc_tm));
328
329		switch (which) {
330			case LEAF_pfTimeoutsTcpFirst:
331				pt.timeout = PFTM_TCP_FIRST_PACKET;
332				break;
333			case LEAF_pfTimeoutsTcpOpening:
334				pt.timeout = PFTM_TCP_OPENING;
335				break;
336			case LEAF_pfTimeoutsTcpEstablished:
337				pt.timeout = PFTM_TCP_ESTABLISHED;
338				break;
339			case LEAF_pfTimeoutsTcpClosing:
340				pt.timeout = PFTM_TCP_CLOSING;
341				break;
342			case LEAF_pfTimeoutsTcpFinWait:
343				pt.timeout = PFTM_TCP_FIN_WAIT;
344				break;
345			case LEAF_pfTimeoutsTcpClosed:
346				pt.timeout = PFTM_TCP_CLOSED;
347				break;
348			case LEAF_pfTimeoutsUdpFirst:
349				pt.timeout = PFTM_UDP_FIRST_PACKET;
350				break;
351			case LEAF_pfTimeoutsUdpSingle:
352				pt.timeout = PFTM_UDP_SINGLE;
353				break;
354			case LEAF_pfTimeoutsUdpMultiple:
355				pt.timeout = PFTM_UDP_MULTIPLE;
356				break;
357			case LEAF_pfTimeoutsIcmpFirst:
358				pt.timeout = PFTM_ICMP_FIRST_PACKET;
359				break;
360			case LEAF_pfTimeoutsIcmpError:
361				pt.timeout = PFTM_ICMP_ERROR_REPLY;
362				break;
363			case LEAF_pfTimeoutsOtherFirst:
364				pt.timeout = PFTM_OTHER_FIRST_PACKET;
365				break;
366			case LEAF_pfTimeoutsOtherSingle:
367				pt.timeout = PFTM_OTHER_SINGLE;
368				break;
369			case LEAF_pfTimeoutsOtherMultiple:
370				pt.timeout = PFTM_OTHER_MULTIPLE;
371				break;
372			case LEAF_pfTimeoutsFragment:
373				pt.timeout = PFTM_FRAG;
374				break;
375			case LEAF_pfTimeoutsInterval:
376				pt.timeout = PFTM_INTERVAL;
377				break;
378			case LEAF_pfTimeoutsAdaptiveStart:
379				pt.timeout = PFTM_ADAPTIVE_START;
380				break;
381			case LEAF_pfTimeoutsAdaptiveEnd:
382				pt.timeout = PFTM_ADAPTIVE_END;
383				break;
384			case LEAF_pfTimeoutsSrcNode:
385				pt.timeout = PFTM_SRC_NODE;
386				break;
387
388			default:
389				return (SNMP_ERR_NOSUCHNAME);
390		}
391
392		if (ioctl(dev, DIOCGETTIMEOUT, &pt)) {
393			syslog(LOG_ERR, "pf_timeouts(): ioctl(): %s",
394			    strerror(errno));
395			return (SNMP_ERR_GENERR);
396		}
397
398		val->v.integer = pt.seconds;
399
400		return (SNMP_ERR_NOERROR);
401	}
402
403	abort();
404}
405
406int
407pf_logif(struct snmp_context __unused *ctx, struct snmp_value *val,
408	u_int sub, u_int __unused vindex, enum snmp_op op)
409{
410	asn_subid_t	which = val->var.subs[sub - 1];
411	unsigned char	str[IFNAMSIZ];
412
413	if (op == SNMP_OP_SET)
414		return (SNMP_ERR_NOT_WRITEABLE);
415
416	if (op == SNMP_OP_GET) {
417		if (pfs_refresh() == -1)
418			return (SNMP_ERR_GENERR);
419
420		switch (which) {
421	 		case LEAF_pfLogInterfaceName:
422				strlcpy(str, pfs.ifname, sizeof str);
423				return (string_get(val, str, strlen(str)));
424			case LEAF_pfLogInterfaceIp4BytesIn:
425				val->v.counter64 = pfs.bcounters[IPV4][IN];
426				break;
427			case LEAF_pfLogInterfaceIp4BytesOut:
428				val->v.counter64 = pfs.bcounters[IPV4][OUT];
429				break;
430			case LEAF_pfLogInterfaceIp4PktsInPass:
431				val->v.counter64 =
432				    pfs.pcounters[IPV4][IN][PF_PASS];
433				break;
434			case LEAF_pfLogInterfaceIp4PktsInDrop:
435				val->v.counter64 =
436				    pfs.pcounters[IPV4][IN][PF_DROP];
437				break;
438			case LEAF_pfLogInterfaceIp4PktsOutPass:
439				val->v.counter64 =
440				    pfs.pcounters[IPV4][OUT][PF_PASS];
441				break;
442			case LEAF_pfLogInterfaceIp4PktsOutDrop:
443				val->v.counter64 =
444				    pfs.pcounters[IPV4][OUT][PF_DROP];
445				break;
446			case LEAF_pfLogInterfaceIp6BytesIn:
447				val->v.counter64 = pfs.bcounters[IPV6][IN];
448				break;
449			case LEAF_pfLogInterfaceIp6BytesOut:
450				val->v.counter64 = pfs.bcounters[IPV6][OUT];
451				break;
452			case LEAF_pfLogInterfaceIp6PktsInPass:
453				val->v.counter64 =
454				    pfs.pcounters[IPV6][IN][PF_PASS];
455				break;
456			case LEAF_pfLogInterfaceIp6PktsInDrop:
457				val->v.counter64 =
458				    pfs.pcounters[IPV6][IN][PF_DROP];
459				break;
460			case LEAF_pfLogInterfaceIp6PktsOutPass:
461				val->v.counter64 =
462				    pfs.pcounters[IPV6][OUT][PF_PASS];
463				break;
464			case LEAF_pfLogInterfaceIp6PktsOutDrop:
465				val->v.counter64 =
466				    pfs.pcounters[IPV6][OUT][PF_DROP];
467				break;
468
469			default:
470				return (SNMP_ERR_NOSUCHNAME);
471		}
472
473		return (SNMP_ERR_NOERROR);
474	}
475
476	abort();
477}
478
479int
480pf_interfaces(struct snmp_context __unused *ctx, struct snmp_value *val,
481	u_int sub, u_int __unused vindex, enum snmp_op op)
482{
483	asn_subid_t	which = val->var.subs[sub - 1];
484
485	if (op == SNMP_OP_SET)
486		return (SNMP_ERR_NOT_WRITEABLE);
487
488	if (op == SNMP_OP_GET) {
489		if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
490			if (pfi_refresh() == -1)
491			    return (SNMP_ERR_GENERR);
492
493		switch (which) {
494			case LEAF_pfInterfacesIfNumber:
495				val->v.uint32 = pfi_table_count;
496				break;
497
498			default:
499				return (SNMP_ERR_NOSUCHNAME);
500		}
501
502		return (SNMP_ERR_NOERROR);
503	}
504
505	abort();
506}
507
508int
509pf_iftable(struct snmp_context __unused *ctx, struct snmp_value *val,
510	u_int sub, u_int __unused vindex, enum snmp_op op)
511{
512	asn_subid_t	which = val->var.subs[sub - 1];
513	struct pfi_entry *e = NULL;
514
515	switch (op) {
516		case SNMP_OP_SET:
517			return (SNMP_ERR_NOT_WRITEABLE);
518		case SNMP_OP_GETNEXT:
519			if ((e = NEXT_OBJECT_INT(&pfi_table,
520			    &val->var, sub)) == NULL)
521				return (SNMP_ERR_NOSUCHNAME);
522			val->var.len = sub + 1;
523			val->var.subs[sub] = e->index;
524			break;
525		case SNMP_OP_GET:
526			if (val->var.len - sub != 1)
527				return (SNMP_ERR_NOSUCHNAME);
528			if ((e = pfi_table_find(val->var.subs[sub])) == NULL)
529				return (SNMP_ERR_NOSUCHNAME);
530			break;
531
532		case SNMP_OP_COMMIT:
533		case SNMP_OP_ROLLBACK:
534		default:
535			abort();
536	}
537
538	if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
539		pfi_refresh();
540
541	switch (which) {
542		case LEAF_pfInterfacesIfDescr:
543			return (string_get(val, e->pfi.pfif_name, -1));
544		case LEAF_pfInterfacesIfType:
545			val->v.integer = PFI_IFTYPE_INSTANCE;
546			break;
547		case LEAF_pfInterfacesIfTZero:
548			val->v.uint32 =
549			    (time(NULL) - e->pfi.pfif_tzero) * 100;
550			break;
551		case LEAF_pfInterfacesIfRefsState:
552			val->v.uint32 = e->pfi.pfif_states;
553			break;
554		case LEAF_pfInterfacesIfRefsRule:
555			val->v.uint32 = e->pfi.pfif_rules;
556			break;
557		case LEAF_pfInterfacesIf4BytesInPass:
558			val->v.counter64 =
559			    e->pfi.pfif_bytes[IPV4][IN][PASS];
560			break;
561		case LEAF_pfInterfacesIf4BytesInBlock:
562			val->v.counter64 =
563			    e->pfi.pfif_bytes[IPV4][IN][BLOCK];
564			break;
565		case LEAF_pfInterfacesIf4BytesOutPass:
566			val->v.counter64 =
567			    e->pfi.pfif_bytes[IPV4][OUT][PASS];
568			break;
569		case LEAF_pfInterfacesIf4BytesOutBlock:
570			val->v.counter64 =
571			    e->pfi.pfif_bytes[IPV4][OUT][BLOCK];
572			break;
573		case LEAF_pfInterfacesIf4PktsInPass:
574			val->v.counter64 =
575			    e->pfi.pfif_packets[IPV4][IN][PASS];
576			break;
577		case LEAF_pfInterfacesIf4PktsInBlock:
578			val->v.counter64 =
579			    e->pfi.pfif_packets[IPV4][IN][BLOCK];
580			break;
581		case LEAF_pfInterfacesIf4PktsOutPass:
582			val->v.counter64 =
583			    e->pfi.pfif_packets[IPV4][OUT][PASS];
584			break;
585		case LEAF_pfInterfacesIf4PktsOutBlock:
586			val->v.counter64 =
587			    e->pfi.pfif_packets[IPV4][OUT][BLOCK];
588			break;
589		case LEAF_pfInterfacesIf6BytesInPass:
590			val->v.counter64 =
591			    e->pfi.pfif_bytes[IPV6][IN][PASS];
592			break;
593		case LEAF_pfInterfacesIf6BytesInBlock:
594			val->v.counter64 =
595			    e->pfi.pfif_bytes[IPV6][IN][BLOCK];
596			break;
597		case LEAF_pfInterfacesIf6BytesOutPass:
598			val->v.counter64 =
599			    e->pfi.pfif_bytes[IPV6][OUT][PASS];
600			break;
601		case LEAF_pfInterfacesIf6BytesOutBlock:
602			val->v.counter64 =
603			    e->pfi.pfif_bytes[IPV6][OUT][BLOCK];
604			break;
605		case LEAF_pfInterfacesIf6PktsInPass:
606			val->v.counter64 =
607			    e->pfi.pfif_packets[IPV6][IN][PASS];
608			break;
609		case LEAF_pfInterfacesIf6PktsInBlock:
610			val->v.counter64 =
611			    e->pfi.pfif_packets[IPV6][IN][BLOCK];
612			break;
613		case LEAF_pfInterfacesIf6PktsOutPass:
614			val->v.counter64 =
615			    e->pfi.pfif_packets[IPV6][OUT][PASS];
616			break;
617		case LEAF_pfInterfacesIf6PktsOutBlock:
618			val->v.counter64 =
619			    e->pfi.pfif_packets[IPV6][OUT][BLOCK];
620			break;
621
622		default:
623			return (SNMP_ERR_NOSUCHNAME);
624	}
625
626	return (SNMP_ERR_NOERROR);
627}
628
629int
630pf_tables(struct snmp_context __unused *ctx, struct snmp_value *val,
631	u_int sub, u_int __unused vindex, enum snmp_op op)
632{
633	asn_subid_t	which = val->var.subs[sub - 1];
634
635	if (op == SNMP_OP_SET)
636		return (SNMP_ERR_NOT_WRITEABLE);
637
638	if (op == SNMP_OP_GET) {
639		if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
640			if (pft_refresh() == -1)
641			    return (SNMP_ERR_GENERR);
642
643		switch (which) {
644			case LEAF_pfTablesTblNumber:
645				val->v.uint32 = pft_table_count;
646				break;
647
648			default:
649				return (SNMP_ERR_NOSUCHNAME);
650		}
651
652		return (SNMP_ERR_NOERROR);
653	}
654
655	abort();
656}
657
658int
659pf_tbltable(struct snmp_context __unused *ctx, struct snmp_value *val,
660	u_int sub, u_int __unused vindex, enum snmp_op op)
661{
662	asn_subid_t	which = val->var.subs[sub - 1];
663	struct pft_entry *e = NULL;
664
665	switch (op) {
666		case SNMP_OP_SET:
667			return (SNMP_ERR_NOT_WRITEABLE);
668		case SNMP_OP_GETNEXT:
669			if ((e = NEXT_OBJECT_INT(&pft_table,
670			    &val->var, sub)) == NULL)
671				return (SNMP_ERR_NOSUCHNAME);
672			val->var.len = sub + 1;
673			val->var.subs[sub] = e->index;
674			break;
675		case SNMP_OP_GET:
676			if (val->var.len - sub != 1)
677				return (SNMP_ERR_NOSUCHNAME);
678			if ((e = pft_table_find(val->var.subs[sub])) == NULL)
679				return (SNMP_ERR_NOSUCHNAME);
680			break;
681
682		case SNMP_OP_COMMIT:
683		case SNMP_OP_ROLLBACK:
684		default:
685			abort();
686	}
687
688	if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
689		pft_refresh();
690
691	switch (which) {
692		case LEAF_pfTablesTblDescr:
693			return (string_get(val, e->pft.pfrts_name, -1));
694		case LEAF_pfTablesTblCount:
695			val->v.integer = e->pft.pfrts_cnt;
696			break;
697		case LEAF_pfTablesTblTZero:
698			val->v.uint32 =
699			    (time(NULL) - e->pft.pfrts_tzero) * 100;
700			break;
701		case LEAF_pfTablesTblRefsAnchor:
702			val->v.integer =
703			    e->pft.pfrts_refcnt[PFR_REFCNT_ANCHOR];
704			break;
705		case LEAF_pfTablesTblRefsRule:
706			val->v.integer =
707			    e->pft.pfrts_refcnt[PFR_REFCNT_RULE];
708			break;
709		case LEAF_pfTablesTblEvalMatch:
710			val->v.counter64 = e->pft.pfrts_match;
711			break;
712		case LEAF_pfTablesTblEvalNoMatch:
713			val->v.counter64 = e->pft.pfrts_nomatch;
714			break;
715		case LEAF_pfTablesTblBytesInPass:
716			val->v.counter64 =
717			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_PASS];
718			break;
719		case LEAF_pfTablesTblBytesInBlock:
720			val->v.counter64 =
721			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_BLOCK];
722			break;
723		case LEAF_pfTablesTblBytesInXPass:
724			val->v.counter64 =
725			    e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_XPASS];
726			break;
727		case LEAF_pfTablesTblBytesOutPass:
728			val->v.counter64 =
729			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_PASS];
730			break;
731		case LEAF_pfTablesTblBytesOutBlock:
732			val->v.counter64 =
733			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_BLOCK];
734			break;
735		case LEAF_pfTablesTblBytesOutXPass:
736			val->v.counter64 =
737			    e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_XPASS];
738			break;
739		case LEAF_pfTablesTblPktsInPass:
740			val->v.counter64 =
741			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_PASS];
742			break;
743		case LEAF_pfTablesTblPktsInBlock:
744			val->v.counter64 =
745			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_BLOCK];
746			break;
747		case LEAF_pfTablesTblPktsInXPass:
748			val->v.counter64 =
749			    e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_XPASS];
750			break;
751		case LEAF_pfTablesTblPktsOutPass:
752			val->v.counter64 =
753			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_PASS];
754			break;
755		case LEAF_pfTablesTblPktsOutBlock:
756			val->v.counter64 =
757			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_BLOCK];
758			break;
759		case LEAF_pfTablesTblPktsOutXPass:
760			val->v.counter64 =
761			    e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_XPASS];
762			break;
763
764		default:
765			return (SNMP_ERR_NOSUCHNAME);
766	}
767
768	return (SNMP_ERR_NOERROR);
769}
770
771int
772pf_tbladdr(struct snmp_context __unused *ctx, struct snmp_value __unused *val,
773	u_int __unused sub, u_int __unused vindex, enum snmp_op __unused op)
774{
775	return (SNMP_ERR_GENERR);
776}
777
778int
779pf_altq(struct snmp_context __unused *ctx, struct snmp_value *val,
780	u_int sub, u_int __unused vindex, enum snmp_op op)
781{
782	asn_subid_t	which = val->var.subs[sub - 1];
783
784	if (op == SNMP_OP_SET)
785		return (SNMP_ERR_NOT_WRITEABLE);
786
787	if (op == SNMP_OP_GET) {
788		if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
789			if (pfq_refresh() == -1)
790			    return (SNMP_ERR_GENERR);
791
792		switch (which) {
793			case LEAF_pfAltqQueueNumber:
794				val->v.uint32 = pfq_table_count;
795				break;
796
797			default:
798				return (SNMP_ERR_NOSUCHNAME);
799		}
800
801		return (SNMP_ERR_NOERROR);
802	}
803
804	abort();
805	return (SNMP_ERR_GENERR);
806}
807
808int
809pf_altqq(struct snmp_context __unused *ctx, struct snmp_value *val,
810	u_int sub, u_int __unused vindex, enum snmp_op op)
811{
812	asn_subid_t	which = val->var.subs[sub - 1];
813	struct pfq_entry *e = NULL;
814
815	switch (op) {
816		case SNMP_OP_SET:
817			return (SNMP_ERR_NOT_WRITEABLE);
818		case SNMP_OP_GETNEXT:
819			if ((e = NEXT_OBJECT_INT(&pfq_table,
820			    &val->var, sub)) == NULL)
821				return (SNMP_ERR_NOSUCHNAME);
822			val->var.len = sub + 1;
823			val->var.subs[sub] = e->index;
824			break;
825		case SNMP_OP_GET:
826			if (val->var.len - sub != 1)
827				return (SNMP_ERR_NOSUCHNAME);
828			if ((e = pfq_table_find(val->var.subs[sub])) == NULL)
829				return (SNMP_ERR_NOSUCHNAME);
830			break;
831
832		case SNMP_OP_COMMIT:
833		case SNMP_OP_ROLLBACK:
834		default:
835			abort();
836	}
837
838	if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
839		pfq_refresh();
840
841	switch (which) {
842		case LEAF_pfAltqQueueDescr:
843			return (string_get(val, e->altq.qname, -1));
844		case LEAF_pfAltqQueueParent:
845			return (string_get(val, e->altq.parent, -1));
846		case LEAF_pfAltqQueueScheduler:
847			val->v.integer = e->altq.scheduler;
848			break;
849		case LEAF_pfAltqQueueBandwidth:
850			val->v.uint32 = e->altq.bandwidth;
851			break;
852		case LEAF_pfAltqQueuePriority:
853			val->v.integer = e->altq.priority;
854			break;
855		case LEAF_pfAltqQueueLimit:
856			val->v.integer = e->altq.qlimit;
857			break;
858
859		default:
860			return (SNMP_ERR_NOSUCHNAME);
861	}
862
863	return (SNMP_ERR_NOERROR);
864}
865
866static struct pfi_entry *
867pfi_table_find(u_int idx)
868{
869	struct pfi_entry *e;
870
871	TAILQ_FOREACH(e, &pfi_table, link)
872		if (e->index == idx)
873			return (e);
874	return (NULL);
875}
876
877static struct pfq_entry *
878pfq_table_find(u_int idx)
879{
880	struct pfq_entry *e;
881	TAILQ_FOREACH(e, &pfq_table, link)
882		if (e->index == idx)
883			return (e);
884	return (NULL);
885}
886
887static struct pft_entry *
888pft_table_find(u_int idx)
889{
890	struct pft_entry *e;
891
892	TAILQ_FOREACH(e, &pft_table, link)
893		if (e->index == idx)
894			return (e);
895	return (NULL);
896}
897
898static int
899pfi_refresh(void)
900{
901	struct pfioc_iface io;
902	struct pfi_if *p;
903	struct pfi_entry *e;
904	int i, numifs = 1;
905
906	if (started && this_tick <= pf_tick)
907		return (0);
908
909	while (!TAILQ_EMPTY(&pfi_table)) {
910		e = TAILQ_FIRST(&pfi_table);
911		TAILQ_REMOVE(&pfi_table, e, link);
912		free(e);
913	}
914
915	bzero(&io, sizeof(io));
916	p = malloc(sizeof(struct pfi_if));
917	io.pfiio_flags = PFI_FLAG_INSTANCE;
918	io.pfiio_esize = sizeof(struct pfi_if);
919
920	for (;;) {
921		p = realloc(p, numifs * sizeof(struct pfi_if));
922		io.pfiio_size = numifs;
923		io.pfiio_buffer = p;
924
925		if (ioctl(dev, DIOCIGETIFACES, &io)) {
926			syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s",
927			    strerror(errno));
928			return (-1);
929		}
930
931		if (numifs >= io.pfiio_size)
932			break;
933
934		numifs = io.pfiio_size;
935	}
936
937	for (i = 0; i < numifs; i++) {
938		e = malloc(sizeof(struct pfi_entry));
939		e->index = i + 1;
940		memcpy(&e->pfi, p+i, sizeof(struct pfi_if));
941		TAILQ_INSERT_TAIL(&pfi_table, e, link);
942	}
943
944	pfi_table_age = time(NULL);
945	pfi_table_count = numifs;
946	pf_tick = this_tick;
947
948	free(p);
949	return (0);
950}
951
952static int
953pfq_refresh(void)
954{
955	struct pfioc_altq pa;
956	struct pfq_entry *e;
957	int i, numqs, ticket;
958
959	if (started && this_tick <= pf_tick)
960		return (0);
961
962	while (!TAILQ_EMPTY(&pfq_table)) {
963		e = TAILQ_FIRST(&pfq_table);
964		TAILQ_REMOVE(&pfq_table, e, link);
965		free(e);
966	}
967
968	bzero(&pa, sizeof(pa));
969
970	if (ioctl(dev, DIOCGETALTQS, &pa)) {
971		syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s",
972		    strerror(errno));
973		return (-1);
974	}
975
976	numqs = pa.nr;
977	ticket = pa.ticket;
978
979	for (i = 0; i < numqs; i++) {
980		e = malloc(sizeof(struct pfq_entry));
981		pa.ticket = ticket;
982		pa.nr = i;
983
984		if (ioctl(dev, DIOCGETALTQ, &pa)) {
985			syslog(LOG_ERR, "pfq_refresh(): "
986			    "ioctl(DIOCGETALTQ): %s",
987			    strerror(errno));
988			return (-1);
989		}
990
991		if (pa.altq.qid > 0) {
992			memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq));
993			e->index = pa.altq.qid;
994			pfq_table_count = i;
995			TAILQ_INSERT_TAIL(&pfq_table, e, link);
996		}
997	}
998
999	pfq_table_age = time(NULL);
1000	pf_tick = this_tick;
1001
1002	return (0);
1003}
1004
1005static int
1006pfs_refresh(void)
1007{
1008	if (started && this_tick <= pf_tick)
1009		return (0);
1010
1011	bzero(&pfs, sizeof(struct pf_status));
1012
1013	if (ioctl(dev, DIOCGETSTATUS, &pfs)) {
1014		syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s",
1015		    strerror(errno));
1016		return (-1);
1017	}
1018
1019	pf_tick = this_tick;
1020	return (0);
1021}
1022
1023static int
1024pft_refresh(void)
1025{
1026	struct pfioc_table io;
1027	struct pfr_tstats *t;
1028	struct pft_entry *e;
1029	int i, numtbls = 1;
1030
1031	if (started && this_tick <= pf_tick)
1032		return (0);
1033
1034	while (!TAILQ_EMPTY(&pft_table)) {
1035		e = TAILQ_FIRST(&pft_table);
1036		TAILQ_REMOVE(&pft_table, e, link);
1037		free(e);
1038	}
1039
1040	bzero(&io, sizeof(io));
1041	t = malloc(sizeof(struct pfr_tstats));
1042	io.pfrio_esize = sizeof(struct pfr_tstats);
1043
1044	for (;;) {
1045		t = realloc(t, numtbls * sizeof(struct pfr_tstats));
1046		io.pfrio_size = numtbls;
1047		io.pfrio_buffer = t;
1048
1049		if (ioctl(dev, DIOCRGETTSTATS, &io)) {
1050			syslog(LOG_ERR, "pft_refresh(): ioctl(): %s",
1051			    strerror(errno));
1052			return (-1);
1053		}
1054
1055		if (numtbls >= io.pfrio_size)
1056			break;
1057
1058		numtbls = io.pfrio_size;
1059	}
1060
1061	for (i = 0; i < numtbls; i++) {
1062		e = malloc(sizeof(struct pfr_tstats));
1063		e->index = i + 1;
1064		memcpy(&e->pft, t+i, sizeof(struct pfr_tstats));
1065		TAILQ_INSERT_TAIL(&pft_table, e, link);
1066	}
1067
1068	pft_table_age = time(NULL);
1069	pft_table_count = numtbls;
1070	pf_tick = this_tick;
1071
1072	free(t);
1073	return (0);
1074}
1075
1076/*
1077 * Implement the bsnmpd module interface
1078 */
1079static int
1080pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[])
1081{
1082	module = mod;
1083
1084	if ((dev = open("/dev/pf", O_RDONLY)) == -1) {
1085		syslog(LOG_ERR, "pf_init(): open(): %s\n",
1086		    strerror(errno));
1087		return (-1);
1088	}
1089
1090	/* Prepare internal state */
1091	TAILQ_INIT(&pfi_table);
1092	TAILQ_INIT(&pfq_table);
1093	TAILQ_INIT(&pft_table);
1094
1095	pfi_refresh();
1096	pfq_refresh();
1097	pfs_refresh();
1098	pft_refresh();
1099
1100	started = 1;
1101
1102	return (0);
1103}
1104
1105static int
1106pf_fini(void)
1107{
1108	struct pfi_entry *i1, *i2;
1109	struct pfq_entry *q1, *q2;
1110	struct pft_entry *t1, *t2;
1111
1112	/* Empty the list of interfaces */
1113	i1 = TAILQ_FIRST(&pfi_table);
1114	while (i1 != NULL) {
1115		i2 = TAILQ_NEXT(i1, link);
1116		free(i1);
1117		i1 = i2;
1118	}
1119
1120	/* List of queues */
1121	q1 = TAILQ_FIRST(&pfq_table);
1122	while (q1 != NULL) {
1123		q2 = TAILQ_NEXT(q1, link);
1124		free(q1);
1125		q1 = q2;
1126	}
1127
1128	/* And the list of tables */
1129	t1 = TAILQ_FIRST(&pft_table);
1130	while (t1 != NULL) {
1131		t2 = TAILQ_NEXT(t1, link);
1132		free(t1);
1133		t1 = t2;
1134	}
1135
1136	close(dev);
1137	return (0);
1138}
1139
1140static void
1141pf_dump(void)
1142{
1143	pfi_refresh();
1144	pfq_refresh();
1145	pft_refresh();
1146
1147	syslog(LOG_ERR, "Dump: pfi_table_age = %jd",
1148	    (intmax_t)pfi_table_age);
1149	syslog(LOG_ERR, "Dump: pfi_table_count = %d",
1150	    pfi_table_count);
1151
1152	syslog(LOG_ERR, "Dump: pfq_table_age = %jd",
1153	    (intmax_t)pfq_table_age);
1154	syslog(LOG_ERR, "Dump: pfq_table_count = %d",
1155	    pfq_table_count);
1156
1157	syslog(LOG_ERR, "Dump: pft_table_age = %jd",
1158	    (intmax_t)pft_table_age);
1159
1160	syslog(LOG_ERR, "Dump: pft_table_count = %d",
1161	    pft_table_count);
1162}
1163
1164const struct snmp_module config = {
1165	.comment = "This module implements a MIB for the pf packet filter.",
1166	.init =		pf_init,
1167	.fini =		pf_fini,
1168	.tree =		pf_ctree,
1169	.dump =		pf_dump,
1170	.tree_size =	pf_CTREE_SIZE,
1171};
1172