From 9eab3572ff0ce21dcb6b6c2764037cebde6c3cb0 Mon Sep 17 00:00:00 2001
From: Phil Preston <philippreston@mac.com>
Date: Tue, 5 Jun 2018 01:58:20 +0100
Subject: [PATCH] Add counter fields to pf input (#4216)

---
 plugins/inputs/pf/README.md  | 15 ++++++++++
 plugins/inputs/pf/pf.go      | 58 +++++++++++++++++++++++++++++-------
 plugins/inputs/pf/pf_test.go | 46 +++++++++++++++++++++++-----
 3 files changed, 101 insertions(+), 18 deletions(-)

diff --git a/plugins/inputs/pf/README.md b/plugins/inputs/pf/README.md
index ed058671..2e70de5b 100644
--- a/plugins/inputs/pf/README.md
+++ b/plugins/inputs/pf/README.md
@@ -31,6 +31,21 @@ telegraf ALL=(root) NOPASSWD: /sbin/pfctl -s info
     - searches (integer, count)
     - inserts (integer, count)
     - removals (integer, count)
+    - match (integer, count)
+    - bad-offset (integer, count)
+    - fragment (integer, count)
+    - short (integer, count)
+    - normalize (integer, count)
+    - memory (integer, count)
+    - bad-timestamp (integer, count)
+    - congestion (integer, count)
+    - ip-option (integer, count)
+    - proto-cksum (integer, count)
+    - state-mismatch (integer, count)
+    - state-insert (integer, count)
+    - state-limit (integer, count)
+    - src-limit (integer, count)
+    - synproxy (integer, count)
 
 ### Example Output:
 
diff --git a/plugins/inputs/pf/pf.go b/plugins/inputs/pf/pf.go
index 9712ee8a..04b5f9d2 100644
--- a/plugins/inputs/pf/pf.go
+++ b/plugins/inputs/pf/pf.go
@@ -67,7 +67,7 @@ func errMissingData(tag string) error {
 
 type pfctlOutputStanza struct {
 	HeaderRE  *regexp.Regexp
-	ParseFunc func([]string, telegraf.Accumulator) error
+	ParseFunc func([]string, map[string]interface{}) error
 	Found     bool
 }
 
@@ -76,11 +76,16 @@ var pfctlOutputStanzas = []*pfctlOutputStanza{
 		HeaderRE:  regexp.MustCompile("^State Table"),
 		ParseFunc: parseStateTable,
 	},
+	&pfctlOutputStanza{
+		HeaderRE:  regexp.MustCompile("^Counters"),
+		ParseFunc: parseCounterTable,
+	},
 }
 
 var anyTableHeaderRE = regexp.MustCompile("^[A-Z]")
 
 func (pf *PF) parsePfctlOutput(pfoutput string, acc telegraf.Accumulator) error {
+	fields := make(map[string]interface{})
 	scanner := bufio.NewScanner(strings.NewReader(pfoutput))
 	for scanner.Scan() {
 		line := scanner.Text()
@@ -91,10 +96,14 @@ func (pf *PF) parsePfctlOutput(pfoutput string, acc telegraf.Accumulator) error
 				line = scanner.Text()
 				for !anyTableHeaderRE.MatchString(line) {
 					stanzaLines = append(stanzaLines, line)
-					scanner.Scan()
-					line = scanner.Text()
+					more := scanner.Scan()
+					if more {
+						line = scanner.Text()
+					} else {
+						break
+					}
 				}
-				if perr := s.ParseFunc(stanzaLines, acc); perr != nil {
+				if perr := s.ParseFunc(stanzaLines, fields); perr != nil {
 					return perr
 				}
 				s.Found = true
@@ -106,6 +115,8 @@ func (pf *PF) parsePfctlOutput(pfoutput string, acc telegraf.Accumulator) error
 			return errParseHeader
 		}
 	}
+
+	acc.AddFields(measurement, fields, make(map[string]string))
 	return nil
 }
 
@@ -124,11 +135,40 @@ var StateTable = []*Entry{
 
 var stateTableRE = regexp.MustCompile(`^  (.*?)\s+(\d+)`)
 
-func parseStateTable(lines []string, acc telegraf.Accumulator) error {
+func parseStateTable(lines []string, fields map[string]interface{}) error {
+	return storeFieldValues(lines, stateTableRE, fields, StateTable)
+}
+
+var CounterTable = []*Entry{
+	&Entry{"match", "match", -1},
+	&Entry{"bad-offset", "bad-offset", -1},
+	&Entry{"fragment", "fragment", -1},
+	&Entry{"short", "short", -1},
+	&Entry{"normalize", "normalize", -1},
+	&Entry{"memory", "memory", -1},
+	&Entry{"bad-timestamp", "bad-timestamp", -1},
+	&Entry{"congestion", "congestion", -1},
+	&Entry{"ip-option", "ip-option", -1},
+	&Entry{"proto-cksum", "proto-cksum", -1},
+	&Entry{"state-mismatch", "state-mismatch", -1},
+	&Entry{"state-insert", "state-insert", -1},
+	&Entry{"state-limit", "state-limit", -1},
+	&Entry{"src-limit", "src-limit", -1},
+	&Entry{"synproxy", "synproxy", -1},
+}
+
+var counterTableRE = regexp.MustCompile(`^  (.*?)\s+(\d+)`)
+
+func parseCounterTable(lines []string, fields map[string]interface{}) error {
+	return storeFieldValues(lines, counterTableRE, fields, CounterTable)
+}
+
+func storeFieldValues(lines []string, regex *regexp.Regexp, fields map[string]interface{}, entryTable []*Entry) error {
+
 	for _, v := range lines {
-		entries := stateTableRE.FindStringSubmatch(v)
+		entries := regex.FindStringSubmatch(v)
 		if entries != nil {
-			for _, f := range StateTable {
+			for _, f := range entryTable {
 				if f.PfctlTitle == entries[1] {
 					var err error
 					if f.Value, err = strconv.ParseInt(entries[2], 10, 64); err != nil {
@@ -139,15 +179,13 @@ func parseStateTable(lines []string, acc telegraf.Accumulator) error {
 		}
 	}
 
-	fields := make(map[string]interface{})
-	for _, v := range StateTable {
+	for _, v := range entryTable {
 		if v.Value == -1 {
 			return errMissingData(v.PfctlTitle)
 		}
 		fields[v.Field] = v.Value
 	}
 
-	acc.AddFields(measurement, fields, make(map[string]string))
 	return nil
 }
 
diff --git a/plugins/inputs/pf/pf_test.go b/plugins/inputs/pf/pf_test.go
index 233e7259..0b90d949 100644
--- a/plugins/inputs/pf/pf_test.go
+++ b/plugins/inputs/pf/pf_test.go
@@ -152,10 +152,25 @@ Counters
 			measurements: []measurementResult{
 				measurementResult{
 					fields: map[string]interface{}{
-						"entries":  int64(2),
-						"searches": int64(11325),
-						"inserts":  int64(5),
-						"removals": int64(3)},
+						"entries":        int64(2),
+						"searches":       int64(11325),
+						"inserts":        int64(5),
+						"removals":       int64(3),
+						"match":          int64(11226),
+						"bad-offset":     int64(0),
+						"fragment":       int64(0),
+						"short":          int64(0),
+						"normalize":      int64(0),
+						"memory":         int64(0),
+						"bad-timestamp":  int64(0),
+						"congestion":     int64(0),
+						"ip-option":      int64(0),
+						"proto-cksum":    int64(0),
+						"state-mismatch": int64(0),
+						"state-insert":   int64(0),
+						"state-limit":    int64(0),
+						"src-limit":      int64(0),
+						"synproxy":       int64(0)},
 					tags: map[string]string{},
 				},
 			},
@@ -197,10 +212,25 @@ Counters
 			measurements: []measurementResult{
 				measurementResult{
 					fields: map[string]interface{}{
-						"entries":  int64(649),
-						"searches": int64(18421725761),
-						"inserts":  int64(156762508),
-						"removals": int64(156761859)},
+						"entries":        int64(649),
+						"searches":       int64(18421725761),
+						"inserts":        int64(156762508),
+						"removals":       int64(156761859),
+						"match":          int64(473002784),
+						"bad-offset":     int64(0),
+						"fragment":       int64(2729),
+						"short":          int64(107),
+						"normalize":      int64(1685),
+						"memory":         int64(101),
+						"bad-timestamp":  int64(0),
+						"congestion":     int64(0),
+						"ip-option":      int64(152301),
+						"proto-cksum":    int64(108),
+						"state-mismatch": int64(24393),
+						"state-insert":   int64(92),
+						"state-limit":    int64(0),
+						"src-limit":      int64(0),
+						"synproxy":       int64(0)},
 					tags: map[string]string{},
 				},
 			},
-- 
GitLab