From owner-freebsd-bugs@FreeBSD.ORG Fri Mar 20 09:20:02 2009 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 155AD1065677 for ; Fri, 20 Mar 2009 09:20:02 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:4f8:fff6::28]) by mx1.freebsd.org (Postfix) with ESMTP id DF1FD8FC16 for ; Fri, 20 Mar 2009 09:20:01 +0000 (UTC) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.14.3/8.14.3) with ESMTP id n2K9K1Jl039281 for ; Fri, 20 Mar 2009 09:20:01 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.3/8.14.3/Submit) id n2K9K1YP039280; Fri, 20 Mar 2009 09:20:01 GMT (envelope-from gnats) Resent-Date: Fri, 20 Mar 2009 09:20:01 GMT Resent-Message-Id: <200903200920.n2K9K1YP039280@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Szalai Andras Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 860FA106566B for ; Fri, 20 Mar 2009 09:15:39 +0000 (UTC) (envelope-from szalai.bandi@gmail.com) Received: from mail-bw0-f164.google.com (mail-bw0-f164.google.com [209.85.218.164]) by mx1.freebsd.org (Postfix) with ESMTP id A24248FC0A for ; Fri, 20 Mar 2009 09:15:38 +0000 (UTC) (envelope-from szalai.bandi@gmail.com) Received: by bwz8 with SMTP id 8so756952bwz.43 for ; Fri, 20 Mar 2009 02:15:37 -0700 (PDT) Received: by 10.223.115.80 with SMTP id h16mr2990196faq.94.1237540537267; Fri, 20 Mar 2009 02:15:37 -0700 (PDT) Received: from tristania.mooo.com (94-21-14-184.pool.digikabel.hu [94.21.14.184]) by mx.google.com with ESMTPS id b17sm3354811fka.24.2009.03.20.02.15.34 (version=TLSv1/SSLv3 cipher=RC4-MD5); Fri, 20 Mar 2009 02:15:35 -0700 (PDT) Received: by tristania.kispest.home (Postfix, from userid 1000) id 27414D4C29; Fri, 20 Mar 2009 10:15:33 +0100 (CET) Message-Id: <20090320091532.GA28471@tristania.kispest.home> Date: Fri, 20 Mar 2009 10:15:32 +0100 From: Szalai Andras To: FreeBSD-gnats-submit@FreeBSD.org X-Send-Pr-Version: 3.113 Cc: Subject: bin/132847: [patch] snmp_pf: add support for retrieving pf filter rule counters X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: Szalai Andras List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 20 Mar 2009 09:20:02 -0000 >Number: 132847 >Category: bin >Synopsis: [patch] snmp_pf: add support for retrieving pf filter rule counters >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: update >Submitter-Id: current-users >Arrival-Date: Fri Mar 20 09:20:01 UTC 2009 >Closed-Date: >Last-Modified: >Originator: Szalai Andras >Release: FreeBSD 7.1-RELEASE-p1 i386 >Organization: none >Environment: System: FreeBSD tristania.kispest.home 7.1-RELEASE-p1 FreeBSD 7.1-RELEASE-p1 #0: Sun Jan 11 17:34:03 CET 2009 root@tristania.kispest.home:/disk/ad8p5/root.usr/src/sys/i386/compile/TRISTANIA i386 >Description: The FreeBSD base system contains a snmp daemon called bsnmpd. It implements most of its functionality via loadable modules. The snmp_pf module is responsible for constructing the MIB for the pf packet filter, however it doesn't support retrieving counters of (labeled) pf filter rules. >How-To-Repeat: >Fix: The attached patch adds a table which contains counters (evaluations, packets in/out, bytes in/out) for each labeled pf filter rule. It can be applied cleanly to snmp_pf from RELENG_7_1. --- snmp_pf.diff begins here --- diff -ru snmp_pf.orig/BEGEMOT-PF-MIB.txt snmp_pf/BEGEMOT-PF-MIB.txt --- snmp_pf.orig/BEGEMOT-PF-MIB.txt 2009-01-10 16:46:38.000000000 +0100 +++ snmp_pf/BEGEMOT-PF-MIB.txt 2009-03-20 09:52:13.000000000 +0100 @@ -51,6 +51,7 @@ pfInterfaces OBJECT IDENTIFIER ::= { begemotPfObjects 8 } pfTables OBJECT IDENTIFIER ::= { begemotPfObjects 9 } pfAltq OBJECT IDENTIFIER ::= { begemotPfObjects 10 } +pfLabels OBJECT IDENTIFIER ::= { begemotPfObjects 11 } -- -------------------------------------------------------------------------- @@ -1227,4 +1228,102 @@ "Maximum number of packets in the queue." ::= { pfAltqQueueEntry 7 } +-- -------------------------------------------------------------------------- + +-- +-- Labeled filter rules statistics +-- + +pfLabelsLblNumber OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of labeled filter rules on this system." + ::= { pfLabels 1 } + +pfLabelsLblTable OBJECT-TYPE + SYNTAX SEQUENCE OF pfLabelsLblEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Table of filter rules, index on pfLabelsLblIndex." + ::= { pfLabels 2 } + +pfLabelsLblEntry OBJECT-TYPE + SYNTAX pfLabelsLblEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Any entry in the pfLabelsLblTable containing information + about a particular filter rule on the system." + INDEX { pfLabelsLblIndex } + ::= { pfLabelsLblTable 1 } + +pfLabelsLblEntry ::= SEQUENCE { + pfLabelsLblIndex Integer32, + pfLabelsLblName OCTET STRING, + pfLabelsLblEvals Counter64, + pfLabelsLblBytesIn Counter64, + pfLabelsLblBytesOut Counter64, + pfLabelsLblPktsIn Counter64, + pfLabelsLblPktsOut Counter64 +} + +pfLabelsLblIndex OBJECT-TYPE + SYNTAX Integer32 (1..2147483647) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A unique value, greater than zero, for each label." + ::= { pfLabelsLblEntry 1 } + +pfLabelsLblName OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The name of the rule label." + ::= { pfLabelsLblEntry 2 } + +pfLabelsLblEvals OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of rule evaluations." + ::= { pfLabelsLblEntry 3 } + +pfLabelsLblBytesIn OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of incoming bytes matched by the rule." + ::= { pfLabelsLblEntry 4 } + +pfLabelsLblBytesOut OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of outgoing bytes matched by the rule." + ::= { pfLabelsLblEntry 5 } + +pfLabelsLblPktsIn OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of incoming packets matched by the rule." + ::= { pfLabelsLblEntry 6 } + +pfLabelsLblPktsOut OBJECT-TYPE + SYNTAX Counter64 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of outgoing packets matched by the rule." + ::= { pfLabelsLblEntry 7 } + END diff -ru snmp_pf.orig/pf_snmp.c snmp_pf/pf_snmp.c --- snmp_pf.orig/pf_snmp.c 2008-11-25 03:59:29.000000000 +0100 +++ snmp_pf/pf_snmp.c 2009-03-20 09:51:57.000000000 +0100 @@ -100,17 +100,38 @@ #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 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 pfl_entry * pfl_table_find(u_int idx); static int altq_is_enabled(int pfdevice); +static int pfl_walk_rulesets(const char *path); +static int pfl_scan_ruleset(const char *path); + int pf_status(struct snmp_context __unused *ctx, struct snmp_value *val, u_int sub, u_int __unused vindex, enum snmp_op op) @@ -773,6 +794,94 @@ } 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(); +} + +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; + + 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(); + } + + if ((time(NULL) - pfl_table_age) > PFL_TABLE_MAXAGE) + pfl_refresh(); + + 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); +} + +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) { @@ -907,6 +1016,17 @@ 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) { @@ -1129,6 +1249,135 @@ return(-1); } +static int +pfl_refresh(void) +{ + struct pfl_entry *e; + + if (started && this_tick <= pf_tick) + return (0); + + pfl_table_count = 0; + while (!TAILQ_EMPTY(&pfl_table)) { + e = TAILQ_FIRST(&pfl_table); + TAILQ_REMOVE(&pfl_table, e, link); + free(e); + } + + if (pfl_walk_rulesets("")) + goto err; + + pfl_table_age = time(NULL); + pf_tick = this_tick; + + return (0); + +err: + pfl_table_count = 0; + while (!TAILQ_EMPTY(&pfl_table)) { + e = TAILQ_FIRST(&pfl_table); + TAILQ_REMOVE(&pfl_table, e, link); + free(e); + } + + 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)) + 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_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); +} + /* * check whether altq support is enabled in kernel */ @@ -1175,6 +1424,7 @@ TAILQ_INIT(&pfi_table); TAILQ_INIT(&pfq_table); TAILQ_INIT(&pft_table); + TAILQ_INIT(&pfl_table); pfi_refresh(); if (altq_enabled) { @@ -1183,6 +1433,7 @@ pfs_refresh(); pft_refresh(); + pfl_refresh(); started = 1; @@ -1195,6 +1446,7 @@ struct pfi_entry *i1, *i2; struct pfq_entry *q1, *q2; struct pft_entry *t1, *t2; + struct pfl_entry *l1, *l2; /* Empty the list of interfaces */ i1 = TAILQ_FIRST(&pfi_table); @@ -1212,7 +1464,7 @@ q1 = q2; } - /* And the list of tables */ + /* List of tables */ t1 = TAILQ_FIRST(&pft_table); while (t1 != NULL) { t2 = TAILQ_NEXT(t1, link); @@ -1220,6 +1472,14 @@ t1 = t2; } + /* 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); } @@ -1232,6 +1492,7 @@ pfq_refresh(); } pft_refresh(); + pfl_refresh(); syslog(LOG_ERR, "Dump: pfi_table_age = %jd", (intmax_t)pfi_table_age); @@ -1245,9 +1506,13 @@ 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: 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 = { diff -ru snmp_pf.orig/pf_tree.def snmp_pf/pf_tree.def --- snmp_pf.orig/pf_tree.def 2009-01-10 16:46:38.000000000 +0100 +++ snmp_pf/pf_tree.def 2009-03-18 09:54:20.000000000 +0100 @@ -186,6 +186,20 @@ ) ) ) + (11 pfLabels + (1 pfLabelsLblNumber INTEGER32 pf_labels GET) + (2 pfLabelsLblTable + (1 pfLabelsLblEntry : INTEGER32 pf_lbltable + (1 pfLabelsLblIndex INTEGER32) + (2 pfLabelsLblName OCTETSTRING GET) + (3 pfLabelsLblEvals COUNTER64 GET) + (4 pfLabelsLblBytesIn COUNTER64 GET) + (5 pfLabelsLblBytesOut COUNTER64 GET) + (6 pfLabelsLblPktsIn COUNTER64 GET) + (7 pfLabelsLblPktsOut COUNTER64 GET) + ) + ) + ) ) ) ) --- snmp_pf.diff ends here --- >Release-Note: >Audit-Trail: >Unformatted: