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