pf_snmp.c revision 179476
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 179476 2008-06-01 14:09:54Z 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_kif	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.pfik_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.pfik_tzero) * 100;
554			break;
555		case LEAF_pfInterfacesIfRefsState:
556			val->v.uint32 = e->pfi.pfik_states;
557			break;
558		case LEAF_pfInterfacesIfRefsRule:
559			val->v.uint32 = e->pfi.pfik_rules;
560			break;
561		case LEAF_pfInterfacesIf4BytesInPass:
562			val->v.counter64 =
563			    e->pfi.pfik_bytes[IPV4][IN][PASS];
564			break;
565		case LEAF_pfInterfacesIf4BytesInBlock:
566			val->v.counter64 =
567			    e->pfi.pfik_bytes[IPV4][IN][BLOCK];
568			break;
569		case LEAF_pfInterfacesIf4BytesOutPass:
570			val->v.counter64 =
571			    e->pfi.pfik_bytes[IPV4][OUT][PASS];
572			break;
573		case LEAF_pfInterfacesIf4BytesOutBlock:
574			val->v.counter64 =
575			    e->pfi.pfik_bytes[IPV4][OUT][BLOCK];
576			break;
577		case LEAF_pfInterfacesIf4PktsInPass:
578			val->v.counter64 =
579			    e->pfi.pfik_packets[IPV4][IN][PASS];
580			break;
581		case LEAF_pfInterfacesIf4PktsInBlock:
582			val->v.counter64 =
583			    e->pfi.pfik_packets[IPV4][IN][BLOCK];
584			break;
585		case LEAF_pfInterfacesIf4PktsOutPass:
586			val->v.counter64 =
587			    e->pfi.pfik_packets[IPV4][OUT][PASS];
588			break;
589		case LEAF_pfInterfacesIf4PktsOutBlock:
590			val->v.counter64 =
591			    e->pfi.pfik_packets[IPV4][OUT][BLOCK];
592			break;
593		case LEAF_pfInterfacesIf6BytesInPass:
594			val->v.counter64 =
595			    e->pfi.pfik_bytes[IPV6][IN][PASS];
596			break;
597		case LEAF_pfInterfacesIf6BytesInBlock:
598			val->v.counter64 =
599			    e->pfi.pfik_bytes[IPV6][IN][BLOCK];
600			break;
601		case LEAF_pfInterfacesIf6BytesOutPass:
602			val->v.counter64 =
603			    e->pfi.pfik_bytes[IPV6][OUT][PASS];
604			break;
605		case LEAF_pfInterfacesIf6BytesOutBlock:
606			val->v.counter64 =
607			    e->pfi.pfik_bytes[IPV6][OUT][BLOCK];
608			break;
609		case LEAF_pfInterfacesIf6PktsInPass:
610			val->v.counter64 =
611			    e->pfi.pfik_packets[IPV6][IN][PASS];
612			break;
613		case LEAF_pfInterfacesIf6PktsInBlock:
614			val->v.counter64 =
615			    e->pfi.pfik_packets[IPV6][IN][BLOCK];
616			break;
617		case LEAF_pfInterfacesIf6PktsOutPass:
618			val->v.counter64 =
619			    e->pfi.pfik_packets[IPV6][OUT][PASS];
620			break;
621		case LEAF_pfInterfacesIf6PktsOutBlock:
622			val->v.counter64 =
623			    e->pfi.pfik_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_kif *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_esize = sizeof(struct pfi_kif);
929
930	for (;;) {
931		p = reallocf(p, numifs * sizeof(struct pfi_kif));
932		if (p == NULL) {
933			syslog(LOG_ERR, "pfi_refresh(): reallocf() numifs=%d: %s",
934			    numifs, strerror(errno));
935			goto err2;
936		}
937		io.pfiio_size = numifs;
938		io.pfiio_buffer = p;
939
940		if (ioctl(dev, DIOCIGETIFACES, &io)) {
941			syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s",
942			    strerror(errno));
943			goto err2;
944		}
945
946		if (numifs >= io.pfiio_size)
947			break;
948
949		numifs = io.pfiio_size;
950	}
951
952	for (i = 0; i < numifs; i++) {
953		e = malloc(sizeof(struct pfi_entry));
954		if (e == NULL)
955			goto err1;
956		e->index = i + 1;
957		memcpy(&e->pfi, p+i, sizeof(struct pfi_kif));
958		TAILQ_INSERT_TAIL(&pfi_table, e, link);
959	}
960
961	pfi_table_age = time(NULL);
962	pfi_table_count = numifs;
963	pf_tick = this_tick;
964
965	free(p);
966	return (0);
967
968err1:
969	while (!TAILQ_EMPTY(&pfi_table)) {
970		e = TAILQ_FIRST(&pfi_table);
971		TAILQ_REMOVE(&pfi_table, e, link);
972		free(e);
973	}
974err2:
975	free(p);
976	return(-1);
977}
978
979static int
980pfq_refresh(void)
981{
982	struct pfioc_altq pa;
983	struct pfq_entry *e;
984	int i, numqs, ticket;
985
986	if (started && this_tick <= pf_tick)
987		return (0);
988
989	while (!TAILQ_EMPTY(&pfq_table)) {
990		e = TAILQ_FIRST(&pfq_table);
991		TAILQ_REMOVE(&pfq_table, e, link);
992		free(e);
993	}
994
995	bzero(&pa, sizeof(pa));
996
997	if (ioctl(dev, DIOCGETALTQS, &pa)) {
998		syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s",
999		    strerror(errno));
1000		return (-1);
1001	}
1002
1003	numqs = pa.nr;
1004	ticket = pa.ticket;
1005
1006	for (i = 0; i < numqs; i++) {
1007		e = malloc(sizeof(struct pfq_entry));
1008		if (e == NULL) {
1009			syslog(LOG_ERR, "pfq_refresh(): "
1010			    "malloc(): %s",
1011			    strerror(errno));
1012			goto err;
1013		}
1014		pa.ticket = ticket;
1015		pa.nr = i;
1016
1017		if (ioctl(dev, DIOCGETALTQ, &pa)) {
1018			syslog(LOG_ERR, "pfq_refresh(): "
1019			    "ioctl(DIOCGETALTQ): %s",
1020			    strerror(errno));
1021			goto err;
1022		}
1023
1024		if (pa.altq.qid > 0) {
1025			memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq));
1026			e->index = pa.altq.qid;
1027			pfq_table_count = i;
1028			INSERT_OBJECT_INT_LINK_INDEX(e, &pfq_table, link, index);
1029		}
1030	}
1031
1032	pfq_table_age = time(NULL);
1033	pf_tick = this_tick;
1034
1035	return (0);
1036err:
1037	free(e);
1038	while (!TAILQ_EMPTY(&pfq_table)) {
1039		e = TAILQ_FIRST(&pfq_table);
1040		TAILQ_REMOVE(&pfq_table, e, link);
1041		free(e);
1042	}
1043	return(-1);
1044}
1045
1046static int
1047pfs_refresh(void)
1048{
1049	if (started && this_tick <= pf_tick)
1050		return (0);
1051
1052	bzero(&pfs, sizeof(struct pf_status));
1053
1054	if (ioctl(dev, DIOCGETSTATUS, &pfs)) {
1055		syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s",
1056		    strerror(errno));
1057		return (-1);
1058	}
1059
1060	pf_tick = this_tick;
1061	return (0);
1062}
1063
1064static int
1065pft_refresh(void)
1066{
1067	struct pfioc_table io;
1068	struct pfr_tstats *t = NULL;
1069	struct pft_entry *e;
1070	int i, numtbls = 1;
1071
1072	if (started && this_tick <= pf_tick)
1073		return (0);
1074
1075	while (!TAILQ_EMPTY(&pft_table)) {
1076		e = TAILQ_FIRST(&pft_table);
1077		TAILQ_REMOVE(&pft_table, e, link);
1078		free(e);
1079	}
1080
1081	bzero(&io, sizeof(io));
1082	io.pfrio_esize = sizeof(struct pfr_tstats);
1083
1084	for (;;) {
1085		t = reallocf(t, numtbls * sizeof(struct pfr_tstats));
1086		if (t == NULL) {
1087			syslog(LOG_ERR, "pft_refresh(): reallocf() numtbls=%d: %s",
1088			    numtbls, strerror(errno));
1089			goto err2;
1090		}
1091		io.pfrio_size = numtbls;
1092		io.pfrio_buffer = t;
1093
1094		if (ioctl(dev, DIOCRGETTSTATS, &io)) {
1095			syslog(LOG_ERR, "pft_refresh(): ioctl(): %s",
1096			    strerror(errno));
1097			goto err2;
1098		}
1099
1100		if (numtbls >= io.pfrio_size)
1101			break;
1102
1103		numtbls = io.pfrio_size;
1104	}
1105
1106	for (i = 0; i < numtbls; i++) {
1107		e = malloc(sizeof(struct pfr_tstats));
1108		if (e == NULL)
1109			goto err1;
1110		e->index = i + 1;
1111		memcpy(&e->pft, t+i, sizeof(struct pfr_tstats));
1112		TAILQ_INSERT_TAIL(&pft_table, e, link);
1113	}
1114
1115	pft_table_age = time(NULL);
1116	pft_table_count = numtbls;
1117	pf_tick = this_tick;
1118
1119	free(t);
1120	return (0);
1121err1:
1122	while (!TAILQ_EMPTY(&pft_table)) {
1123		e = TAILQ_FIRST(&pft_table);
1124		TAILQ_REMOVE(&pft_table, e, link);
1125		free(e);
1126	}
1127err2:
1128	free(t);
1129	return(-1);
1130}
1131
1132/*
1133 * check whether altq support is enabled in kernel
1134 */
1135
1136static int
1137altq_is_enabled(int pfdev)
1138{
1139        struct pfioc_altq pa;
1140
1141	errno = 0;
1142        if (ioctl(pfdev, DIOCGETALTQS, &pa)) {
1143                if (errno == ENODEV) {
1144			syslog(LOG_INFO, "No ALTQ support in kernel\n"
1145			    "ALTQ related functions disabled\n");
1146                        return (0);
1147                } else
1148                        syslog(LOG_ERR, "DIOCGETALTQS returned an error: %s",
1149			    strerror(errno));
1150			return (-1);
1151        }
1152        return (1);
1153}
1154
1155/*
1156 * Implement the bsnmpd module interface
1157 */
1158static int
1159pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[])
1160{
1161	module = mod;
1162
1163	if ((dev = open("/dev/pf", O_RDONLY)) == -1) {
1164		syslog(LOG_ERR, "pf_init(): open(): %s\n",
1165		    strerror(errno));
1166		return (-1);
1167	}
1168
1169	if ((altq_enabled = altq_is_enabled(dev)) == -1) {
1170		syslog(LOG_ERR, "pf_init(): altq test failed");
1171		return (-1);
1172	}
1173
1174	/* Prepare internal state */
1175	TAILQ_INIT(&pfi_table);
1176	TAILQ_INIT(&pfq_table);
1177	TAILQ_INIT(&pft_table);
1178
1179	pfi_refresh();
1180	if (altq_enabled) {
1181		pfq_refresh();
1182	}
1183
1184	pfs_refresh();
1185	pft_refresh();
1186
1187	started = 1;
1188
1189	return (0);
1190}
1191
1192static int
1193pf_fini(void)
1194{
1195	struct pfi_entry *i1, *i2;
1196	struct pfq_entry *q1, *q2;
1197	struct pft_entry *t1, *t2;
1198
1199	/* Empty the list of interfaces */
1200	i1 = TAILQ_FIRST(&pfi_table);
1201	while (i1 != NULL) {
1202		i2 = TAILQ_NEXT(i1, link);
1203		free(i1);
1204		i1 = i2;
1205	}
1206
1207	/* List of queues */
1208	q1 = TAILQ_FIRST(&pfq_table);
1209	while (q1 != NULL) {
1210		q2 = TAILQ_NEXT(q1, link);
1211		free(q1);
1212		q1 = q2;
1213	}
1214
1215	/* And the list of tables */
1216	t1 = TAILQ_FIRST(&pft_table);
1217	while (t1 != NULL) {
1218		t2 = TAILQ_NEXT(t1, link);
1219		free(t1);
1220		t1 = t2;
1221	}
1222
1223	close(dev);
1224	return (0);
1225}
1226
1227static void
1228pf_dump(void)
1229{
1230	pfi_refresh();
1231	if (altq_enabled) {
1232		pfq_refresh();
1233	}
1234	pft_refresh();
1235
1236	syslog(LOG_ERR, "Dump: pfi_table_age = %jd",
1237	    (intmax_t)pfi_table_age);
1238	syslog(LOG_ERR, "Dump: pfi_table_count = %d",
1239	    pfi_table_count);
1240
1241	syslog(LOG_ERR, "Dump: pfq_table_age = %jd",
1242	    (intmax_t)pfq_table_age);
1243	syslog(LOG_ERR, "Dump: pfq_table_count = %d",
1244	    pfq_table_count);
1245
1246	syslog(LOG_ERR, "Dump: pft_table_age = %jd",
1247	    (intmax_t)pft_table_age);
1248
1249	syslog(LOG_ERR, "Dump: pft_table_count = %d",
1250	    pft_table_count);
1251}
1252
1253const struct snmp_module config = {
1254	.comment = "This module implements a MIB for the pf packet filter.",
1255	.init =		pf_init,
1256	.fini =		pf_fini,
1257	.tree =		pf_ctree,
1258	.dump =		pf_dump,
1259	.tree_size =	pf_CTREE_SIZE,
1260};
1261