pf_snmp.c revision 149571
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 149571 2005-08-29 10:08:04Z 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 = NULL;
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	io.pfiio_flags = PFI_FLAG_INSTANCE;
917	io.pfiio_esize = sizeof(struct pfi_if);
918
919	for (;;) {
920		p = reallocf(p, numifs * sizeof(struct pfi_if));
921		if (p == NULL) {
922			syslog(LOG_ERR, "pfi_refresh(): reallocf() numifs=%d: %s",
923			    numifs, strerror(errno));
924			goto err2;
925		}
926		io.pfiio_size = numifs;
927		io.pfiio_buffer = p;
928
929		if (ioctl(dev, DIOCIGETIFACES, &io)) {
930			syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s",
931			    strerror(errno));
932			goto err2;
933		}
934
935		if (numifs >= io.pfiio_size)
936			break;
937
938		numifs = io.pfiio_size;
939	}
940
941	for (i = 0; i < numifs; i++) {
942		e = malloc(sizeof(struct pfi_entry));
943		if (e == NULL)
944			goto err1;
945		e->index = i + 1;
946		memcpy(&e->pfi, p+i, sizeof(struct pfi_if));
947		TAILQ_INSERT_TAIL(&pfi_table, e, link);
948	}
949
950	pfi_table_age = time(NULL);
951	pfi_table_count = numifs;
952	pf_tick = this_tick;
953
954	free(p);
955	return (0);
956
957err1:
958	while (!TAILQ_EMPTY(&pfi_table)) {
959		e = TAILQ_FIRST(&pfi_table);
960		TAILQ_REMOVE(&pfi_table, e, link);
961		free(e);
962	}
963err2:
964	free(p);
965	return(-1);
966}
967
968static int
969pfq_refresh(void)
970{
971	struct pfioc_altq pa;
972	struct pfq_entry *e;
973	int i, numqs, ticket;
974
975	if (started && this_tick <= pf_tick)
976		return (0);
977
978	while (!TAILQ_EMPTY(&pfq_table)) {
979		e = TAILQ_FIRST(&pfq_table);
980		TAILQ_REMOVE(&pfq_table, e, link);
981		free(e);
982	}
983
984	bzero(&pa, sizeof(pa));
985
986	if (ioctl(dev, DIOCGETALTQS, &pa)) {
987		syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s",
988		    strerror(errno));
989		return (-1);
990	}
991
992	numqs = pa.nr;
993	ticket = pa.ticket;
994
995	for (i = 0; i < numqs; i++) {
996		e = malloc(sizeof(struct pfq_entry));
997		if (e == NULL) {
998			syslog(LOG_ERR, "pfq_refresh(): "
999			    "malloc(): %s",
1000			    strerror(errno));
1001			goto err;
1002		}
1003		pa.ticket = ticket;
1004		pa.nr = i;
1005
1006		if (ioctl(dev, DIOCGETALTQ, &pa)) {
1007			syslog(LOG_ERR, "pfq_refresh(): "
1008			    "ioctl(DIOCGETALTQ): %s",
1009			    strerror(errno));
1010			goto err;
1011		}
1012
1013		if (pa.altq.qid > 0) {
1014			memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq));
1015			e->index = pa.altq.qid;
1016			pfq_table_count = i;
1017			TAILQ_INSERT_TAIL(&pfq_table, e, link);
1018		}
1019	}
1020
1021	pfq_table_age = time(NULL);
1022	pf_tick = this_tick;
1023
1024	return (0);
1025err:
1026	free(e);
1027	while (!TAILQ_EMPTY(&pfq_table)) {
1028		e = TAILQ_FIRST(&pfq_table);
1029		TAILQ_REMOVE(&pfq_table, e, link);
1030		free(e);
1031	}
1032	return(-1);
1033}
1034
1035static int
1036pfs_refresh(void)
1037{
1038	if (started && this_tick <= pf_tick)
1039		return (0);
1040
1041	bzero(&pfs, sizeof(struct pf_status));
1042
1043	if (ioctl(dev, DIOCGETSTATUS, &pfs)) {
1044		syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s",
1045		    strerror(errno));
1046		return (-1);
1047	}
1048
1049	pf_tick = this_tick;
1050	return (0);
1051}
1052
1053static int
1054pft_refresh(void)
1055{
1056	struct pfioc_table io;
1057	struct pfr_tstats *t = NULL;
1058	struct pft_entry *e;
1059	int i, numtbls = 1;
1060
1061	if (started && this_tick <= pf_tick)
1062		return (0);
1063
1064	while (!TAILQ_EMPTY(&pft_table)) {
1065		e = TAILQ_FIRST(&pft_table);
1066		TAILQ_REMOVE(&pft_table, e, link);
1067		free(e);
1068	}
1069
1070	bzero(&io, sizeof(io));
1071	io.pfrio_esize = sizeof(struct pfr_tstats);
1072
1073	for (;;) {
1074		t = reallocf(t, numtbls * sizeof(struct pfr_tstats));
1075		if (t == NULL) {
1076			syslog(LOG_ERR, "pft_refresh(): reallocf() numtbls=%d: %s",
1077			    numtbls, strerror(errno));
1078			goto err2;
1079		}
1080		io.pfrio_size = numtbls;
1081		io.pfrio_buffer = t;
1082
1083		if (ioctl(dev, DIOCRGETTSTATS, &io)) {
1084			syslog(LOG_ERR, "pft_refresh(): ioctl(): %s",
1085			    strerror(errno));
1086			goto err2;
1087		}
1088
1089		if (numtbls >= io.pfrio_size)
1090			break;
1091
1092		numtbls = io.pfrio_size;
1093	}
1094
1095	for (i = 0; i < numtbls; i++) {
1096		e = malloc(sizeof(struct pfr_tstats));
1097		if (e == NULL)
1098			goto err1;
1099		e->index = i + 1;
1100		memcpy(&e->pft, t+i, sizeof(struct pfr_tstats));
1101		TAILQ_INSERT_TAIL(&pft_table, e, link);
1102	}
1103
1104	pft_table_age = time(NULL);
1105	pft_table_count = numtbls;
1106	pf_tick = this_tick;
1107
1108	free(t);
1109	return (0);
1110err1:
1111	while (!TAILQ_EMPTY(&pft_table)) {
1112		e = TAILQ_FIRST(&pft_table);
1113		TAILQ_REMOVE(&pft_table, e, link);
1114		free(e);
1115	}
1116err2:
1117	free(t);
1118	return(-1);
1119}
1120
1121/*
1122 * Implement the bsnmpd module interface
1123 */
1124static int
1125pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[])
1126{
1127	module = mod;
1128
1129	if ((dev = open("/dev/pf", O_RDONLY)) == -1) {
1130		syslog(LOG_ERR, "pf_init(): open(): %s\n",
1131		    strerror(errno));
1132		return (-1);
1133	}
1134
1135	/* Prepare internal state */
1136	TAILQ_INIT(&pfi_table);
1137	TAILQ_INIT(&pfq_table);
1138	TAILQ_INIT(&pft_table);
1139
1140	pfi_refresh();
1141	pfq_refresh();
1142	pfs_refresh();
1143	pft_refresh();
1144
1145	started = 1;
1146
1147	return (0);
1148}
1149
1150static int
1151pf_fini(void)
1152{
1153	struct pfi_entry *i1, *i2;
1154	struct pfq_entry *q1, *q2;
1155	struct pft_entry *t1, *t2;
1156
1157	/* Empty the list of interfaces */
1158	i1 = TAILQ_FIRST(&pfi_table);
1159	while (i1 != NULL) {
1160		i2 = TAILQ_NEXT(i1, link);
1161		free(i1);
1162		i1 = i2;
1163	}
1164
1165	/* List of queues */
1166	q1 = TAILQ_FIRST(&pfq_table);
1167	while (q1 != NULL) {
1168		q2 = TAILQ_NEXT(q1, link);
1169		free(q1);
1170		q1 = q2;
1171	}
1172
1173	/* And the list of tables */
1174	t1 = TAILQ_FIRST(&pft_table);
1175	while (t1 != NULL) {
1176		t2 = TAILQ_NEXT(t1, link);
1177		free(t1);
1178		t1 = t2;
1179	}
1180
1181	close(dev);
1182	return (0);
1183}
1184
1185static void
1186pf_dump(void)
1187{
1188	pfi_refresh();
1189	pfq_refresh();
1190	pft_refresh();
1191
1192	syslog(LOG_ERR, "Dump: pfi_table_age = %jd",
1193	    (intmax_t)pfi_table_age);
1194	syslog(LOG_ERR, "Dump: pfi_table_count = %d",
1195	    pfi_table_count);
1196
1197	syslog(LOG_ERR, "Dump: pfq_table_age = %jd",
1198	    (intmax_t)pfq_table_age);
1199	syslog(LOG_ERR, "Dump: pfq_table_count = %d",
1200	    pfq_table_count);
1201
1202	syslog(LOG_ERR, "Dump: pft_table_age = %jd",
1203	    (intmax_t)pft_table_age);
1204
1205	syslog(LOG_ERR, "Dump: pft_table_count = %d",
1206	    pft_table_count);
1207}
1208
1209const struct snmp_module config = {
1210	.comment = "This module implements a MIB for the pf packet filter.",
1211	.init =		pf_init,
1212	.fini =		pf_fini,
1213	.tree =		pf_ctree,
1214	.dump =		pf_dump,
1215	.tree_size =	pf_CTREE_SIZE,
1216};
1217