/*- * Copyright (c) 2005 Philip Paeps * 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 #include #include #include #include #include #include #include #include #include #include #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 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 /* Forward declarations */ static int pfi_refresh(void); static int pfq_refresh(void); static int pfs_refresh(void); static int pft_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 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; 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(); } if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE) pfi_refresh(); 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; 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(); } if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE) pft_refresh(); 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) { return (SNMP_ERR_GENERR); } 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_NOERROR); } 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_NOERROR); } 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(); } if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE) pfq_refresh(); 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); } 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 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 pfr_tstats)); 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); } /* * 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); pfi_refresh(); if (altq_enabled) { pfq_refresh(); } pfs_refresh(); pft_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; /* 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; } /* And the list of tables */ t1 = TAILQ_FIRST(&pft_table); while (t1 != NULL) { t2 = TAILQ_NEXT(t1, link); free(t1); t1 = t2; } close(dev); return (0); } static void pf_dump(void) { pfi_refresh(); if (altq_enabled) { pfq_refresh(); } pft_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); } 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, };