diff options
Diffstat (limited to 'usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c')
-rw-r--r-- | usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c | 1800 |
1 files changed, 1800 insertions, 0 deletions
diff --git a/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c b/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c new file mode 100644 index 0000000..bc4bc35 --- /dev/null +++ b/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c @@ -0,0 +1,1800 @@ +/*- + * Copyright (c) 2005 Philip Paeps <philip@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <bsnmp/snmpmod.h> + +#include <net/pfvar.h> +#include <sys/ioctl.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +#include "pf_oid.h" +#include "pf_tree.h" + +struct lmodule *module; + +static int dev = -1; +static int started; +static uint64_t pf_tick; + +static struct pf_status pfs; + +enum { IN, OUT }; +enum { IPV4, IPV6 }; +enum { PASS, BLOCK }; + +#define PFI_IFTYPE_GROUP 0 +#define PFI_IFTYPE_INSTANCE 1 +#define PFI_IFTYPE_DETACHED 2 + +struct pfi_entry { + struct pfi_kif pfi; + u_int index; + TAILQ_ENTRY(pfi_entry) link; +}; +TAILQ_HEAD(pfi_table, pfi_entry); + +static struct pfi_table pfi_table; +static time_t pfi_table_age; +static int pfi_table_count; + +#define PFI_TABLE_MAXAGE 5 + +struct pft_entry { + struct pfr_tstats pft; + u_int index; + TAILQ_ENTRY(pft_entry) link; +}; +TAILQ_HEAD(pft_table, pft_entry); + +static struct pft_table pft_table; +static time_t pft_table_age; +static int pft_table_count; + +#define PFT_TABLE_MAXAGE 5 + +struct pfa_entry { + struct pfr_astats pfas; + u_int index; + TAILQ_ENTRY(pfa_entry) link; +}; +TAILQ_HEAD(pfa_table, pfa_entry); + +static struct pfa_table pfa_table; +static time_t pfa_table_age; +static int pfa_table_count; + +#define PFA_TABLE_MAXAGE 5 + +struct pfq_entry { + struct pf_altq altq; + u_int index; + TAILQ_ENTRY(pfq_entry) link; +}; +TAILQ_HEAD(pfq_table, pfq_entry); + +static struct pfq_table pfq_table; +static time_t pfq_table_age; +static int pfq_table_count; + +static int altq_enabled = 0; + +#define PFQ_TABLE_MAXAGE 5 + +struct pfl_entry { + char name[MAXPATHLEN + PF_RULE_LABEL_SIZE]; + u_int64_t evals; + u_int64_t bytes[2]; + u_int64_t pkts[2]; + u_int index; + TAILQ_ENTRY(pfl_entry) link; +}; +TAILQ_HEAD(pfl_table, pfl_entry); + +static struct pfl_table pfl_table; +static time_t pfl_table_age; +static int pfl_table_count; + +#define PFL_TABLE_MAXAGE 5 + +/* Forward declarations */ +static int pfi_refresh(void); +static int pfq_refresh(void); +static int pfs_refresh(void); +static int pft_refresh(void); +static int pfa_refresh(void); +static int pfl_refresh(void); +static struct pfi_entry * pfi_table_find(u_int idx); +static struct pfq_entry * pfq_table_find(u_int idx); +static struct pft_entry * pft_table_find(u_int idx); +static struct pfa_entry * pfa_table_find(u_int idx); +static struct pfl_entry * pfl_table_find(u_int idx); + +static int altq_is_enabled(int pfdevice); + +int +pf_status(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + time_t runtime; + unsigned char str[128]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if (pfs_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfStatusRunning: + val->v.uint32 = pfs.running; + break; + case LEAF_pfStatusRuntime: + runtime = (pfs.since > 0) ? + time(NULL) - pfs.since : 0; + val->v.uint32 = runtime * 100; + break; + case LEAF_pfStatusDebug: + val->v.uint32 = pfs.debug; + break; + case LEAF_pfStatusHostId: + sprintf(str, "0x%08x", ntohl(pfs.hostid)); + return (string_get(val, str, strlen(str))); + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_counter(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if (pfs_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfCounterMatch: + val->v.counter64 = pfs.counters[PFRES_MATCH]; + break; + case LEAF_pfCounterBadOffset: + val->v.counter64 = pfs.counters[PFRES_BADOFF]; + break; + case LEAF_pfCounterFragment: + val->v.counter64 = pfs.counters[PFRES_FRAG]; + break; + case LEAF_pfCounterShort: + val->v.counter64 = pfs.counters[PFRES_SHORT]; + break; + case LEAF_pfCounterNormalize: + val->v.counter64 = pfs.counters[PFRES_NORM]; + break; + case LEAF_pfCounterMemDrop: + val->v.counter64 = pfs.counters[PFRES_MEMORY]; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_statetable(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if (pfs_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfStateTableCount: + val->v.uint32 = pfs.states; + break; + case LEAF_pfStateTableSearches: + val->v.counter64 = + pfs.fcounters[FCNT_STATE_SEARCH]; + break; + case LEAF_pfStateTableInserts: + val->v.counter64 = + pfs.fcounters[FCNT_STATE_INSERT]; + break; + case LEAF_pfStateTableRemovals: + val->v.counter64 = + pfs.fcounters[FCNT_STATE_REMOVALS]; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_srcnodes(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if (pfs_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfSrcNodesCount: + val->v.uint32 = pfs.src_nodes; + break; + case LEAF_pfSrcNodesSearches: + val->v.counter64 = + pfs.scounters[SCNT_SRC_NODE_SEARCH]; + break; + case LEAF_pfSrcNodesInserts: + val->v.counter64 = + pfs.scounters[SCNT_SRC_NODE_INSERT]; + break; + case LEAF_pfSrcNodesRemovals: + val->v.counter64 = + pfs.scounters[SCNT_SRC_NODE_REMOVALS]; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_limits(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pfioc_limit pl; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + bzero(&pl, sizeof(struct pfioc_limit)); + + switch (which) { + case LEAF_pfLimitsStates: + pl.index = PF_LIMIT_STATES; + break; + case LEAF_pfLimitsSrcNodes: + pl.index = PF_LIMIT_SRC_NODES; + break; + case LEAF_pfLimitsFrags: + pl.index = PF_LIMIT_FRAGS; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + if (ioctl(dev, DIOCGETLIMIT, &pl)) { + syslog(LOG_ERR, "pf_limits(): ioctl(): %s", + strerror(errno)); + return (SNMP_ERR_GENERR); + } + + val->v.uint32 = pl.limit; + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_timeouts(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pfioc_tm pt; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + bzero(&pt, sizeof(struct pfioc_tm)); + + switch (which) { + case LEAF_pfTimeoutsTcpFirst: + pt.timeout = PFTM_TCP_FIRST_PACKET; + break; + case LEAF_pfTimeoutsTcpOpening: + pt.timeout = PFTM_TCP_OPENING; + break; + case LEAF_pfTimeoutsTcpEstablished: + pt.timeout = PFTM_TCP_ESTABLISHED; + break; + case LEAF_pfTimeoutsTcpClosing: + pt.timeout = PFTM_TCP_CLOSING; + break; + case LEAF_pfTimeoutsTcpFinWait: + pt.timeout = PFTM_TCP_FIN_WAIT; + break; + case LEAF_pfTimeoutsTcpClosed: + pt.timeout = PFTM_TCP_CLOSED; + break; + case LEAF_pfTimeoutsUdpFirst: + pt.timeout = PFTM_UDP_FIRST_PACKET; + break; + case LEAF_pfTimeoutsUdpSingle: + pt.timeout = PFTM_UDP_SINGLE; + break; + case LEAF_pfTimeoutsUdpMultiple: + pt.timeout = PFTM_UDP_MULTIPLE; + break; + case LEAF_pfTimeoutsIcmpFirst: + pt.timeout = PFTM_ICMP_FIRST_PACKET; + break; + case LEAF_pfTimeoutsIcmpError: + pt.timeout = PFTM_ICMP_ERROR_REPLY; + break; + case LEAF_pfTimeoutsOtherFirst: + pt.timeout = PFTM_OTHER_FIRST_PACKET; + break; + case LEAF_pfTimeoutsOtherSingle: + pt.timeout = PFTM_OTHER_SINGLE; + break; + case LEAF_pfTimeoutsOtherMultiple: + pt.timeout = PFTM_OTHER_MULTIPLE; + break; + case LEAF_pfTimeoutsFragment: + pt.timeout = PFTM_FRAG; + break; + case LEAF_pfTimeoutsInterval: + pt.timeout = PFTM_INTERVAL; + break; + case LEAF_pfTimeoutsAdaptiveStart: + pt.timeout = PFTM_ADAPTIVE_START; + break; + case LEAF_pfTimeoutsAdaptiveEnd: + pt.timeout = PFTM_ADAPTIVE_END; + break; + case LEAF_pfTimeoutsSrcNode: + pt.timeout = PFTM_SRC_NODE; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + if (ioctl(dev, DIOCGETTIMEOUT, &pt)) { + syslog(LOG_ERR, "pf_timeouts(): ioctl(): %s", + strerror(errno)); + return (SNMP_ERR_GENERR); + } + + val->v.integer = pt.seconds; + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_logif(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + unsigned char str[IFNAMSIZ]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if (pfs_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfLogInterfaceName: + strlcpy(str, pfs.ifname, sizeof str); + return (string_get(val, str, strlen(str))); + case LEAF_pfLogInterfaceIp4BytesIn: + val->v.counter64 = pfs.bcounters[IPV4][IN]; + break; + case LEAF_pfLogInterfaceIp4BytesOut: + val->v.counter64 = pfs.bcounters[IPV4][OUT]; + break; + case LEAF_pfLogInterfaceIp4PktsInPass: + val->v.counter64 = + pfs.pcounters[IPV4][IN][PF_PASS]; + break; + case LEAF_pfLogInterfaceIp4PktsInDrop: + val->v.counter64 = + pfs.pcounters[IPV4][IN][PF_DROP]; + break; + case LEAF_pfLogInterfaceIp4PktsOutPass: + val->v.counter64 = + pfs.pcounters[IPV4][OUT][PF_PASS]; + break; + case LEAF_pfLogInterfaceIp4PktsOutDrop: + val->v.counter64 = + pfs.pcounters[IPV4][OUT][PF_DROP]; + break; + case LEAF_pfLogInterfaceIp6BytesIn: + val->v.counter64 = pfs.bcounters[IPV6][IN]; + break; + case LEAF_pfLogInterfaceIp6BytesOut: + val->v.counter64 = pfs.bcounters[IPV6][OUT]; + break; + case LEAF_pfLogInterfaceIp6PktsInPass: + val->v.counter64 = + pfs.pcounters[IPV6][IN][PF_PASS]; + break; + case LEAF_pfLogInterfaceIp6PktsInDrop: + val->v.counter64 = + pfs.pcounters[IPV6][IN][PF_DROP]; + break; + case LEAF_pfLogInterfaceIp6PktsOutPass: + val->v.counter64 = + pfs.pcounters[IPV6][OUT][PF_PASS]; + break; + case LEAF_pfLogInterfaceIp6PktsOutDrop: + val->v.counter64 = + pfs.pcounters[IPV6][OUT][PF_DROP]; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_interfaces(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE) + if (pfi_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfInterfacesIfNumber: + val->v.uint32 = pfi_table_count; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_iftable(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pfi_entry *e = NULL; + + if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE) + pfi_refresh(); + + switch (op) { + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_GETNEXT: + if ((e = NEXT_OBJECT_INT(&pfi_table, + &val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + val->var.len = sub + 1; + val->var.subs[sub] = e->index; + break; + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((e = pfi_table_find(val->var.subs[sub])) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_COMMIT: + case SNMP_OP_ROLLBACK: + default: + abort(); + } + + switch (which) { + case LEAF_pfInterfacesIfDescr: + return (string_get(val, e->pfi.pfik_name, -1)); + case LEAF_pfInterfacesIfType: + val->v.integer = PFI_IFTYPE_INSTANCE; + break; + case LEAF_pfInterfacesIfTZero: + val->v.uint32 = + (time(NULL) - e->pfi.pfik_tzero) * 100; + break; + case LEAF_pfInterfacesIfRefsState: + val->v.uint32 = e->pfi.pfik_states; + break; + case LEAF_pfInterfacesIfRefsRule: + val->v.uint32 = e->pfi.pfik_rules; + break; + case LEAF_pfInterfacesIf4BytesInPass: + val->v.counter64 = + e->pfi.pfik_bytes[IPV4][IN][PASS]; + break; + case LEAF_pfInterfacesIf4BytesInBlock: + val->v.counter64 = + e->pfi.pfik_bytes[IPV4][IN][BLOCK]; + break; + case LEAF_pfInterfacesIf4BytesOutPass: + val->v.counter64 = + e->pfi.pfik_bytes[IPV4][OUT][PASS]; + break; + case LEAF_pfInterfacesIf4BytesOutBlock: + val->v.counter64 = + e->pfi.pfik_bytes[IPV4][OUT][BLOCK]; + break; + case LEAF_pfInterfacesIf4PktsInPass: + val->v.counter64 = + e->pfi.pfik_packets[IPV4][IN][PASS]; + break; + case LEAF_pfInterfacesIf4PktsInBlock: + val->v.counter64 = + e->pfi.pfik_packets[IPV4][IN][BLOCK]; + break; + case LEAF_pfInterfacesIf4PktsOutPass: + val->v.counter64 = + e->pfi.pfik_packets[IPV4][OUT][PASS]; + break; + case LEAF_pfInterfacesIf4PktsOutBlock: + val->v.counter64 = + e->pfi.pfik_packets[IPV4][OUT][BLOCK]; + break; + case LEAF_pfInterfacesIf6BytesInPass: + val->v.counter64 = + e->pfi.pfik_bytes[IPV6][IN][PASS]; + break; + case LEAF_pfInterfacesIf6BytesInBlock: + val->v.counter64 = + e->pfi.pfik_bytes[IPV6][IN][BLOCK]; + break; + case LEAF_pfInterfacesIf6BytesOutPass: + val->v.counter64 = + e->pfi.pfik_bytes[IPV6][OUT][PASS]; + break; + case LEAF_pfInterfacesIf6BytesOutBlock: + val->v.counter64 = + e->pfi.pfik_bytes[IPV6][OUT][BLOCK]; + break; + case LEAF_pfInterfacesIf6PktsInPass: + val->v.counter64 = + e->pfi.pfik_packets[IPV6][IN][PASS]; + break; + case LEAF_pfInterfacesIf6PktsInBlock: + val->v.counter64 = + e->pfi.pfik_packets[IPV6][IN][BLOCK]; + break; + case LEAF_pfInterfacesIf6PktsOutPass: + val->v.counter64 = + e->pfi.pfik_packets[IPV6][OUT][PASS]; + break; + case LEAF_pfInterfacesIf6PktsOutBlock: + val->v.counter64 = + e->pfi.pfik_packets[IPV6][OUT][BLOCK]; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); +} + +int +pf_tables(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE) + if (pft_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfTablesTblNumber: + val->v.uint32 = pft_table_count; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); +} + +int +pf_tbltable(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pft_entry *e = NULL; + + if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE) + pft_refresh(); + + switch (op) { + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_GETNEXT: + if ((e = NEXT_OBJECT_INT(&pft_table, + &val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + val->var.len = sub + 1; + val->var.subs[sub] = e->index; + break; + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((e = pft_table_find(val->var.subs[sub])) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_COMMIT: + case SNMP_OP_ROLLBACK: + default: + abort(); + } + + switch (which) { + case LEAF_pfTablesTblDescr: + return (string_get(val, e->pft.pfrts_name, -1)); + case LEAF_pfTablesTblCount: + val->v.integer = e->pft.pfrts_cnt; + break; + case LEAF_pfTablesTblTZero: + val->v.uint32 = + (time(NULL) - e->pft.pfrts_tzero) * 100; + break; + case LEAF_pfTablesTblRefsAnchor: + val->v.integer = + e->pft.pfrts_refcnt[PFR_REFCNT_ANCHOR]; + break; + case LEAF_pfTablesTblRefsRule: + val->v.integer = + e->pft.pfrts_refcnt[PFR_REFCNT_RULE]; + break; + case LEAF_pfTablesTblEvalMatch: + val->v.counter64 = e->pft.pfrts_match; + break; + case LEAF_pfTablesTblEvalNoMatch: + val->v.counter64 = e->pft.pfrts_nomatch; + break; + case LEAF_pfTablesTblBytesInPass: + val->v.counter64 = + e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_PASS]; + break; + case LEAF_pfTablesTblBytesInBlock: + val->v.counter64 = + e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesTblBytesInXPass: + val->v.counter64 = + e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_XPASS]; + break; + case LEAF_pfTablesTblBytesOutPass: + val->v.counter64 = + e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_PASS]; + break; + case LEAF_pfTablesTblBytesOutBlock: + val->v.counter64 = + e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesTblBytesOutXPass: + val->v.counter64 = + e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_XPASS]; + break; + case LEAF_pfTablesTblPktsInPass: + val->v.counter64 = + e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_PASS]; + break; + case LEAF_pfTablesTblPktsInBlock: + val->v.counter64 = + e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesTblPktsInXPass: + val->v.counter64 = + e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_XPASS]; + break; + case LEAF_pfTablesTblPktsOutPass: + val->v.counter64 = + e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_PASS]; + break; + case LEAF_pfTablesTblPktsOutBlock: + val->v.counter64 = + e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesTblPktsOutXPass: + val->v.counter64 = + e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_XPASS]; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); +} + +int +pf_tbladdr(struct snmp_context __unused *ctx, struct snmp_value __unused *val, + u_int __unused sub, u_int __unused vindex, enum snmp_op __unused op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pfa_entry *e = NULL; + + if ((time(NULL) - pfa_table_age) > PFA_TABLE_MAXAGE) + pfa_refresh(); + + switch (op) { + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_GETNEXT: + if ((e = NEXT_OBJECT_INT(&pfa_table, + &val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + val->var.len = sub + 1; + val->var.subs[sub] = e->index; + break; + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((e = pfa_table_find(val->var.subs[sub])) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_COMMIT: + case SNMP_OP_ROLLBACK: + default: + abort(); + } + + switch (which) { + case LEAF_pfTablesAddrNetType: + if (e->pfas.pfras_a.pfra_af == AF_INET) + val->v.integer = pfTablesAddrNetType_ipv4; + else if (e->pfas.pfras_a.pfra_af == AF_INET6) + val->v.integer = pfTablesAddrNetType_ipv6; + else + return (SNMP_ERR_GENERR); + break; + case LEAF_pfTablesAddrNet: + if (e->pfas.pfras_a.pfra_af == AF_INET) { + return (string_get(val, + (u_char *)&e->pfas.pfras_a.pfra_ip4addr, 4)); + } else if (e->pfas.pfras_a.pfra_af == AF_INET6) + return (string_get(val, + (u_char *)&e->pfas.pfras_a.pfra_ip6addr, 16)); + else + return (SNMP_ERR_GENERR); + break; + case LEAF_pfTablesAddrPrefix: + val->v.integer = (int32_t) e->pfas.pfras_a.pfra_net; + break; + case LEAF_pfTablesAddrTZero: + val->v.uint32 = + (time(NULL) - e->pfas.pfras_tzero) * 100; + break; + case LEAF_pfTablesAddrBytesInPass: + val->v.counter64 = + e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_PASS]; + break; + case LEAF_pfTablesAddrBytesInBlock: + val->v.counter64 = + e->pfas.pfras_bytes[PFR_DIR_IN][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesAddrBytesOutPass: + val->v.counter64 = + e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_PASS]; + break; + case LEAF_pfTablesAddrBytesOutBlock: + val->v.counter64 = + e->pfas.pfras_bytes[PFR_DIR_OUT][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesAddrPktsInPass: + val->v.counter64 = + e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_PASS]; + break; + case LEAF_pfTablesAddrPktsInBlock: + val->v.counter64 = + e->pfas.pfras_packets[PFR_DIR_IN][PFR_OP_BLOCK]; + break; + case LEAF_pfTablesAddrPktsOutPass: + val->v.counter64 = + e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_PASS]; + break; + case LEAF_pfTablesAddrPktsOutBlock: + val->v.counter64 = + e->pfas.pfras_packets[PFR_DIR_OUT][PFR_OP_BLOCK]; + break; + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); +} + +int +pf_altq(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (!altq_enabled) + return (SNMP_ERR_NOSUCHNAME); + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE) + if (pfq_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfAltqQueueNumber: + val->v.uint32 = pfq_table_count; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); + return (SNMP_ERR_GENERR); +} + +int +pf_altqq(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pfq_entry *e = NULL; + + if (!altq_enabled) + return (SNMP_ERR_NOSUCHNAME); + + if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE) + pfq_refresh(); + + switch (op) { + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_GETNEXT: + if ((e = NEXT_OBJECT_INT(&pfq_table, + &val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + val->var.len = sub + 1; + val->var.subs[sub] = e->index; + break; + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((e = pfq_table_find(val->var.subs[sub])) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_COMMIT: + case SNMP_OP_ROLLBACK: + default: + abort(); + } + + switch (which) { + case LEAF_pfAltqQueueDescr: + return (string_get(val, e->altq.qname, -1)); + case LEAF_pfAltqQueueParent: + return (string_get(val, e->altq.parent, -1)); + case LEAF_pfAltqQueueScheduler: + val->v.integer = e->altq.scheduler; + break; + case LEAF_pfAltqQueueBandwidth: + val->v.uint32 = e->altq.bandwidth; + break; + case LEAF_pfAltqQueuePriority: + val->v.integer = e->altq.priority; + break; + case LEAF_pfAltqQueueLimit: + val->v.integer = e->altq.qlimit; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); +} + +int +pf_labels(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + + if (op == SNMP_OP_SET) + return (SNMP_ERR_NOT_WRITEABLE); + + if (op == SNMP_OP_GET) { + if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE) + if (pfl_refresh() == -1) + return (SNMP_ERR_GENERR); + + switch (which) { + case LEAF_pfLabelsLblNumber: + val->v.uint32 = pfl_table_count; + break; + + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); + } + + abort(); + return (SNMP_ERR_GENERR); +} + +int +pf_lbltable(struct snmp_context __unused *ctx, struct snmp_value *val, + u_int sub, u_int __unused vindex, enum snmp_op op) +{ + asn_subid_t which = val->var.subs[sub - 1]; + struct pfl_entry *e = NULL; + + if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE) + pfl_refresh(); + + switch (op) { + case SNMP_OP_SET: + return (SNMP_ERR_NOT_WRITEABLE); + case SNMP_OP_GETNEXT: + if ((e = NEXT_OBJECT_INT(&pfl_table, + &val->var, sub)) == NULL) + return (SNMP_ERR_NOSUCHNAME); + val->var.len = sub + 1; + val->var.subs[sub] = e->index; + break; + case SNMP_OP_GET: + if (val->var.len - sub != 1) + return (SNMP_ERR_NOSUCHNAME); + if ((e = pfl_table_find(val->var.subs[sub])) == NULL) + return (SNMP_ERR_NOSUCHNAME); + break; + + case SNMP_OP_COMMIT: + case SNMP_OP_ROLLBACK: + default: + abort(); + } + + switch (which) { + case LEAF_pfLabelsLblName: + return (string_get(val, e->name, -1)); + case LEAF_pfLabelsLblEvals: + val->v.counter64 = e->evals; + break; + case LEAF_pfLabelsLblBytesIn: + val->v.counter64 = e->bytes[IN]; + break; + case LEAF_pfLabelsLblBytesOut: + val->v.counter64 = e->bytes[OUT]; + break; + case LEAF_pfLabelsLblPktsIn: + val->v.counter64 = e->pkts[IN]; + break; + case LEAF_pfLabelsLblPktsOut: + val->v.counter64 = e->pkts[OUT]; + break; + default: + return (SNMP_ERR_NOSUCHNAME); + } + + return (SNMP_ERR_NOERROR); +} + +static struct pfi_entry * +pfi_table_find(u_int idx) +{ + struct pfi_entry *e; + + TAILQ_FOREACH(e, &pfi_table, link) + if (e->index == idx) + return (e); + return (NULL); +} + +static struct pfq_entry * +pfq_table_find(u_int idx) +{ + struct pfq_entry *e; + + TAILQ_FOREACH(e, &pfq_table, link) + if (e->index == idx) + return (e); + return (NULL); +} + +static struct pft_entry * +pft_table_find(u_int idx) +{ + struct pft_entry *e; + + TAILQ_FOREACH(e, &pft_table, link) + if (e->index == idx) + return (e); + return (NULL); +} + +static struct pfa_entry * +pfa_table_find(u_int idx) +{ + struct pfa_entry *e; + + TAILQ_FOREACH(e, &pfa_table, link) + if (e->index == idx) + return (e); + return (NULL); +} + +static struct pfl_entry * +pfl_table_find(u_int idx) +{ + struct pfl_entry *e; + + TAILQ_FOREACH(e, &pfl_table, link) + if (e->index == idx) + return (e); + + return (NULL); +} + +static int +pfi_refresh(void) +{ + struct pfioc_iface io; + struct pfi_kif *p = NULL; + struct pfi_entry *e; + int i, numifs = 1; + + if (started && this_tick <= pf_tick) + return (0); + + while (!TAILQ_EMPTY(&pfi_table)) { + e = TAILQ_FIRST(&pfi_table); + TAILQ_REMOVE(&pfi_table, e, link); + free(e); + } + + bzero(&io, sizeof(io)); + io.pfiio_esize = sizeof(struct pfi_kif); + + for (;;) { + p = reallocf(p, numifs * sizeof(struct pfi_kif)); + if (p == NULL) { + syslog(LOG_ERR, "pfi_refresh(): reallocf() numifs=%d: %s", + numifs, strerror(errno)); + goto err2; + } + io.pfiio_size = numifs; + io.pfiio_buffer = p; + + if (ioctl(dev, DIOCIGETIFACES, &io)) { + syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s", + strerror(errno)); + goto err2; + } + + if (numifs >= io.pfiio_size) + break; + + numifs = io.pfiio_size; + } + + for (i = 0; i < numifs; i++) { + e = malloc(sizeof(struct pfi_entry)); + if (e == NULL) + goto err1; + e->index = i + 1; + memcpy(&e->pfi, p+i, sizeof(struct pfi_kif)); + TAILQ_INSERT_TAIL(&pfi_table, e, link); + } + + pfi_table_age = time(NULL); + pfi_table_count = numifs; + pf_tick = this_tick; + + free(p); + return (0); + +err1: + while (!TAILQ_EMPTY(&pfi_table)) { + e = TAILQ_FIRST(&pfi_table); + TAILQ_REMOVE(&pfi_table, e, link); + free(e); + } +err2: + free(p); + return(-1); +} + +static int +pfq_refresh(void) +{ + struct pfioc_altq pa; + struct pfq_entry *e; + int i, numqs, ticket; + + if (started && this_tick <= pf_tick) + return (0); + + while (!TAILQ_EMPTY(&pfq_table)) { + e = TAILQ_FIRST(&pfq_table); + TAILQ_REMOVE(&pfq_table, e, link); + free(e); + } + + bzero(&pa, sizeof(pa)); + + if (ioctl(dev, DIOCGETALTQS, &pa)) { + syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s", + strerror(errno)); + return (-1); + } + + numqs = pa.nr; + ticket = pa.ticket; + + for (i = 0; i < numqs; i++) { + e = malloc(sizeof(struct pfq_entry)); + if (e == NULL) { + syslog(LOG_ERR, "pfq_refresh(): " + "malloc(): %s", + strerror(errno)); + goto err; + } + pa.ticket = ticket; + pa.nr = i; + + if (ioctl(dev, DIOCGETALTQ, &pa)) { + syslog(LOG_ERR, "pfq_refresh(): " + "ioctl(DIOCGETALTQ): %s", + strerror(errno)); + goto err; + } + + if (pa.altq.qid > 0) { + memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq)); + e->index = pa.altq.qid; + pfq_table_count = i; + INSERT_OBJECT_INT_LINK_INDEX(e, &pfq_table, link, index); + } + } + + pfq_table_age = time(NULL); + pf_tick = this_tick; + + return (0); +err: + free(e); + while (!TAILQ_EMPTY(&pfq_table)) { + e = TAILQ_FIRST(&pfq_table); + TAILQ_REMOVE(&pfq_table, e, link); + free(e); + } + return(-1); +} + +static int +pfs_refresh(void) +{ + if (started && this_tick <= pf_tick) + return (0); + + bzero(&pfs, sizeof(struct pf_status)); + + if (ioctl(dev, DIOCGETSTATUS, &pfs)) { + syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s", + strerror(errno)); + return (-1); + } + + pf_tick = this_tick; + return (0); +} + +static int +pft_refresh(void) +{ + struct pfioc_table io; + struct pfr_tstats *t = NULL; + struct pft_entry *e; + int i, numtbls = 1; + + if (started && this_tick <= pf_tick) + return (0); + + while (!TAILQ_EMPTY(&pft_table)) { + e = TAILQ_FIRST(&pft_table); + TAILQ_REMOVE(&pft_table, e, link); + free(e); + } + + bzero(&io, sizeof(io)); + io.pfrio_esize = sizeof(struct pfr_tstats); + + for (;;) { + t = reallocf(t, numtbls * sizeof(struct pfr_tstats)); + if (t == NULL) { + syslog(LOG_ERR, "pft_refresh(): reallocf() numtbls=%d: %s", + numtbls, strerror(errno)); + goto err2; + } + io.pfrio_size = numtbls; + io.pfrio_buffer = t; + + if (ioctl(dev, DIOCRGETTSTATS, &io)) { + syslog(LOG_ERR, "pft_refresh(): ioctl(): %s", + strerror(errno)); + goto err2; + } + + if (numtbls >= io.pfrio_size) + break; + + numtbls = io.pfrio_size; + } + + for (i = 0; i < numtbls; i++) { + e = malloc(sizeof(struct pft_entry)); + if (e == NULL) + goto err1; + e->index = i + 1; + memcpy(&e->pft, t+i, sizeof(struct pfr_tstats)); + TAILQ_INSERT_TAIL(&pft_table, e, link); + } + + pft_table_age = time(NULL); + pft_table_count = numtbls; + pf_tick = this_tick; + + free(t); + return (0); +err1: + while (!TAILQ_EMPTY(&pft_table)) { + e = TAILQ_FIRST(&pft_table); + TAILQ_REMOVE(&pft_table, e, link); + free(e); + } +err2: + free(t); + return(-1); +} + +static int +pfa_table_addrs(u_int sidx, struct pfr_table *pt) +{ + struct pfioc_table io; + struct pfr_astats *t = NULL; + struct pfa_entry *e; + int i, numaddrs = 1; + + if (pt == NULL) + return (-1); + + memset(&io, 0, sizeof(io)); + strlcpy(io.pfrio_table.pfrt_name, pt->pfrt_name, + sizeof(io.pfrio_table.pfrt_name)); + + for (;;) { + t = reallocf(t, numaddrs * sizeof(struct pfr_astats)); + if (t == NULL) { + syslog(LOG_ERR, "pfa_table_addrs(): reallocf(): %s", + strerror(errno)); + numaddrs = -1; + goto error; + } + + memset(t, 0, sizeof(*t)); + io.pfrio_size = numaddrs; + io.pfrio_buffer = t; + io.pfrio_esize = sizeof(struct pfr_astats); + + if (ioctl(dev, DIOCRGETASTATS, &io)) { + syslog(LOG_ERR, "pfa_table_addrs(): ioctl() on %s: %s", + pt->pfrt_name, strerror(errno)); + numaddrs = -1; + break; + } + + if (numaddrs >= io.pfrio_size) + break; + + numaddrs = io.pfrio_size; + } + + for (i = 0; i < numaddrs; i++) { + if ((t + i)->pfras_a.pfra_af != AF_INET && + (t + i)->pfras_a.pfra_af != AF_INET6) { + numaddrs = i; + break; + } + + e = (struct pfa_entry *)malloc(sizeof(struct pfa_entry)); + if (e == NULL) { + syslog(LOG_ERR, "pfa_table_addrs(): malloc(): %s", + strerror(errno)); + numaddrs = -1; + break; + } + e->index = sidx + i; + memcpy(&e->pfas, t + i, sizeof(struct pfr_astats)); + TAILQ_INSERT_TAIL(&pfa_table, e, link); + } + + free(t); +error: + return (numaddrs); +} + +static int +pfa_refresh(void) +{ + struct pfioc_table io; + struct pfr_table *pt = NULL, *it = NULL; + struct pfa_entry *e; + int i, numtbls = 1, cidx, naddrs; + + if (started && this_tick <= pf_tick) + return (0); + + while (!TAILQ_EMPTY(&pfa_table)) { + e = TAILQ_FIRST(&pfa_table); + TAILQ_REMOVE(&pfa_table, e, link); + free(e); + } + + memset(&io, 0, sizeof(io)); + io.pfrio_esize = sizeof(struct pfr_table); + + for (;;) { + pt = reallocf(pt, numtbls * sizeof(struct pfr_table)); + if (pt == NULL) { + syslog(LOG_ERR, "pfa_refresh(): reallocf() %s", + strerror(errno)); + return (-1); + } + memset(pt, 0, sizeof(*pt)); + io.pfrio_size = numtbls; + io.pfrio_buffer = pt; + + if (ioctl(dev, DIOCRGETTABLES, &io)) { + syslog(LOG_ERR, "pfa_refresh(): ioctl(): %s", + strerror(errno)); + goto err2; + } + + if (numtbls >= io.pfrio_size) + break; + + numtbls = io.pfrio_size; + } + + cidx = 1; + + for (it = pt, i = 0; i < numtbls; it++, i++) { + /* + * Skip the table if not active - ioctl(DIOCRGETASTATS) will + * return ESRCH for this entry anyway. + */ + if (!(it->pfrt_flags & PFR_TFLAG_ACTIVE)) + continue; + + if ((naddrs = pfa_table_addrs(cidx, it)) < 0) + goto err1; + + cidx += naddrs; + } + + pfa_table_age = time(NULL); + pfa_table_count = cidx; + pf_tick = this_tick; + + free(pt); + return (0); +err1: + while (!TAILQ_EMPTY(&pfa_table)) { + e = TAILQ_FIRST(&pfa_table); + TAILQ_REMOVE(&pfa_table, e, link); + free(e); + } + +err2: + free(pt); + return (-1); +} + +static int +pfl_scan_ruleset(const char *path) +{ + struct pfioc_rule pr; + struct pfl_entry *e; + u_int32_t nr, i; + + bzero(&pr, sizeof(pr)); + strlcpy(pr.anchor, path, sizeof(pr.anchor)); + pr.rule.action = PF_PASS; + if (ioctl(dev, DIOCGETRULES, &pr)) { + syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULES): %s", + strerror(errno)); + goto err; + } + + for (nr = pr.nr, i = 0; i < nr; i++) { + pr.nr = i; + if (ioctl(dev, DIOCGETRULE, &pr)) { + syslog(LOG_ERR, "pfl_scan_ruleset: ioctl(DIOCGETRULE):" + " %s", strerror(errno)); + goto err; + } + + if (pr.rule.label[0]) { + e = (struct pfl_entry *)malloc(sizeof(*e)); + if (e == NULL) + goto err; + + strlcpy(e->name, path, sizeof(e->name)); + if (path[0]) + strlcat(e->name, "/", sizeof(e->name)); + strlcat(e->name, pr.rule.label, sizeof(e->name)); + + e->evals = pr.rule.evaluations; + e->bytes[IN] = pr.rule.bytes[IN]; + e->bytes[OUT] = pr.rule.bytes[OUT]; + e->pkts[IN] = pr.rule.packets[IN]; + e->pkts[OUT] = pr.rule.packets[OUT]; + e->index = ++pfl_table_count; + + TAILQ_INSERT_TAIL(&pfl_table, e, link); + } + } + + return (0); + +err: + return (-1); +} + +static int +pfl_walk_rulesets(const char *path) +{ + struct pfioc_ruleset prs; + char newpath[MAXPATHLEN]; + u_int32_t nr, i; + + if (pfl_scan_ruleset(path)) + goto err; + + bzero(&prs, sizeof(prs)); + strlcpy(prs.path, path, sizeof(prs.path)); + if (ioctl(dev, DIOCGETRULESETS, &prs)) { + syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESETS): %s", + strerror(errno)); + goto err; + } + + for (nr = prs.nr, i = 0; i < nr; i++) { + prs.nr = i; + if (ioctl(dev, DIOCGETRULESET, &prs)) { + syslog(LOG_ERR, "pfl_walk_rulesets: ioctl(DIOCGETRULESET):" + " %s", strerror(errno)); + goto err; + } + + if (strcmp(prs.name, PF_RESERVED_ANCHOR) == 0) + continue; + + strlcpy(newpath, path, sizeof(newpath)); + if (path[0]) + strlcat(newpath, "/", sizeof(newpath)); + + strlcat(newpath, prs.name, sizeof(newpath)); + if (pfl_walk_rulesets(newpath)) + goto err; + } + + return (0); + +err: + return (-1); +} + +static int +pfl_refresh(void) +{ + struct pfl_entry *e; + + if (started && this_tick <= pf_tick) + return (0); + + while (!TAILQ_EMPTY(&pfl_table)) { + e = TAILQ_FIRST(&pfl_table); + TAILQ_REMOVE(&pfl_table, e, link); + free(e); + } + pfl_table_count = 0; + + if (pfl_walk_rulesets("")) + goto err; + + pfl_table_age = time(NULL); + pf_tick = this_tick; + + return (0); + +err: + while (!TAILQ_EMPTY(&pfl_table)) { + e = TAILQ_FIRST(&pfl_table); + TAILQ_REMOVE(&pfl_table, e, link); + free(e); + } + pfl_table_count = 0; + + return (-1); +} + +/* + * check whether altq support is enabled in kernel + */ + +static int +altq_is_enabled(int pfdev) +{ + struct pfioc_altq pa; + + errno = 0; + if (ioctl(pfdev, DIOCGETALTQS, &pa)) { + if (errno == ENODEV) { + syslog(LOG_INFO, "No ALTQ support in kernel\n" + "ALTQ related functions disabled\n"); + return (0); + } else + syslog(LOG_ERR, "DIOCGETALTQS returned an error: %s", + strerror(errno)); + return (-1); + } + return (1); +} + +/* + * Implement the bsnmpd module interface + */ +static int +pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[]) +{ + module = mod; + + if ((dev = open("/dev/pf", O_RDONLY)) == -1) { + syslog(LOG_ERR, "pf_init(): open(): %s\n", + strerror(errno)); + return (-1); + } + + if ((altq_enabled = altq_is_enabled(dev)) == -1) { + syslog(LOG_ERR, "pf_init(): altq test failed"); + return (-1); + } + + /* Prepare internal state */ + TAILQ_INIT(&pfi_table); + TAILQ_INIT(&pfq_table); + TAILQ_INIT(&pft_table); + TAILQ_INIT(&pfa_table); + TAILQ_INIT(&pfl_table); + + pfi_refresh(); + if (altq_enabled) { + pfq_refresh(); + } + + pfs_refresh(); + pft_refresh(); + pfa_refresh(); + pfl_refresh(); + + started = 1; + + return (0); +} + +static int +pf_fini(void) +{ + struct pfi_entry *i1, *i2; + struct pfq_entry *q1, *q2; + struct pft_entry *t1, *t2; + struct pfa_entry *a1, *a2; + struct pfl_entry *l1, *l2; + + /* Empty the list of interfaces */ + i1 = TAILQ_FIRST(&pfi_table); + while (i1 != NULL) { + i2 = TAILQ_NEXT(i1, link); + free(i1); + i1 = i2; + } + + /* List of queues */ + q1 = TAILQ_FIRST(&pfq_table); + while (q1 != NULL) { + q2 = TAILQ_NEXT(q1, link); + free(q1); + q1 = q2; + } + + /* List of tables */ + t1 = TAILQ_FIRST(&pft_table); + while (t1 != NULL) { + t2 = TAILQ_NEXT(t1, link); + free(t1); + t1 = t2; + } + + /* List of table addresses */ + a1 = TAILQ_FIRST(&pfa_table); + while (a1 != NULL) { + a2 = TAILQ_NEXT(a1, link); + free(a1); + a1 = a2; + } + + /* And the list of labeled filter rules */ + l1 = TAILQ_FIRST(&pfl_table); + while (l1 != NULL) { + l2 = TAILQ_NEXT(l1, link); + free(l1); + l1 = l2; + } + + close(dev); + return (0); +} + +static void +pf_dump(void) +{ + pfi_refresh(); + if (altq_enabled) { + pfq_refresh(); + } + pft_refresh(); + pfa_refresh(); + pfl_refresh(); + + syslog(LOG_ERR, "Dump: pfi_table_age = %jd", + (intmax_t)pfi_table_age); + syslog(LOG_ERR, "Dump: pfi_table_count = %d", + pfi_table_count); + + syslog(LOG_ERR, "Dump: pfq_table_age = %jd", + (intmax_t)pfq_table_age); + syslog(LOG_ERR, "Dump: pfq_table_count = %d", + pfq_table_count); + + syslog(LOG_ERR, "Dump: pft_table_age = %jd", + (intmax_t)pft_table_age); + syslog(LOG_ERR, "Dump: pft_table_count = %d", + pft_table_count); + + syslog(LOG_ERR, "Dump: pfa_table_age = %jd", + (intmax_t)pfa_table_age); + syslog(LOG_ERR, "Dump: pfa_table_count = %d", + pfa_table_count); + + syslog(LOG_ERR, "Dump: pfl_table_age = %jd", + (intmax_t)pfl_table_age); + syslog(LOG_ERR, "Dump: pfl_table_count = %d", + pfl_table_count); +} + +const struct snmp_module config = { + .comment = "This module implements a MIB for the pf packet filter.", + .init = pf_init, + .fini = pf_fini, + .tree = pf_ctree, + .dump = pf_dump, + .tree_size = pf_CTREE_SIZE, +}; |